smturner1 has asked for the wisdom of the Perl Monks concerning the following question:

I am creating a new Perl script to handle a web code deploy. In the following snippet of code I am trying to remove all of the files and folders in a specified directory. EPIC is saying that I have a syntax error on the 'sub selective_delete {' line. I have no idea why. Additionally, since I am new if you could look at the code to see if it is an efficient way to perform what I am trying to do or let me know if it will or will not work I would appreciate it. My goal is to create clean efficient code.

use strict; use warnings; use autodie; use File::Path qw(make_path, rmtree); #Set vars my $destinationDir = 'C:\Users\Shaun\Documents\$website'; my $file; #Set File/Folders to exclude from delete my @KEEP = ( 'Prg421 pdf', ); #Set routine to do selective delete sub selective_delete { opendir my $deletes, $destinationDir; #Set params my @files = glob( "$deletes/*" ); my @files = ( readdir $deleletes ); #loop through directory copying all files and folders to $del for dele +tion FILE: while (my$file = @files) { for my $kp (@KEEP) { next FILE if $file eq $kp; } rmtree($deletes); } return; }

Replies are listed 'Best First'.
Re: Syntax Error deleting files and folders in a directory
by NetWallah (Canon) on Dec 28, 2013 at 18:21 UTC
    Welcome to the Monastery!
    • Since you use "qw" in the "use File::Path", remove the comma after make_path
    • Correct the "opendir" to : opendir my ( $deletes) , $destinationDir;
    • @files is declared twice - figure out which method you want to use.
    • The readdir is used to read only ONE line from the $deletes dir handle . No error/EOF checking
    • $deletes is misspelled in "readdir $deleletes"
    • The $deletes dir handle is not closed
    • Code indentation could be improved

            If your eyes hurt after you drink coffee, you have to take the spoon out of the cup.
                  -Norm Crosby

      You missed one...

      • while (my$file = @files) {...} is an infinite loop, and the variable $file will just be a number, not a filename. The intention was probably something like while (my $file = shift @files) {...}, though the following would be safer: while (@files) {my $file = shift @files; ...} (just in case there is a file named "0").
      use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
        Aah - yes - I had not read that file down ... thanks.

        Also - these (completely incorrect) loops:

        FILE: while (my$file = @files) { for my $kp (@KEEP) { next FILE if $file eq $kp; } rmtree($deletes); } return; }
        can be written in a more perlishly as :
        my %keepers = map {$_=>1} @KEEP ; # OR declare %KEEP instead of @KEEP rmtree ([grep {not $keepers{$_} }@files]);
        (untested).

        You probably need to chomp(@files) too, before this operation.

        File::Path documentation prefers remove_tree over the legacy rmtree.

                If your eyes hurt after you drink coffee, you have to take the spoon out of the cup.
                      -Norm Crosby

        Thank you Tobyinc

      Thank you for your input. I corrected all of the bullet points you suggested with one exception; my @files is declared twice because I want to copy files and directories from the $destinationDir to the $deletes var. Can I do this with only one of the methods @files?

      use strict; use warnings; use autodie; use File::Path qw(make_path rmtree); #Set vars my $destinationDir = 'C:\Users\Shaun\Documents\$website'; my $file; #Set File/Folders to exclude from delete my @KEEP = ( 'Prg421 pdf', ); #Set routine to do selective delete sub selective_delete { opendir my ($deletes), $destinationDir; #Set params my @files = glob( "$deletes/*" ); my @files = ( readdir $deletes ); #loop through directory copying all files and folders to $del for dele +tion FILE: while (my$file = @files) { for my $kp (@KEEP) { next FILE if $file eq $kp; } rmtree($deletes); } return; }

        You know how troublesome it is when there are two (or more) people in the room with you who have the same name and you want to address only one of them? Well Perl has the same trouble with variables. You have introduced two array variables with the same name in the same room (for room read: lexical scope (set of {})) - kinda like having identical twins dressed and groomed the same with the same name in the room. Perl warns you about that, then just uses one of them completely ignoring the other.

        Actually your whole script is still full of bugs. I'll let you go off and read the rest of the replies you got so you can fix some of them yourself. Come back if you don't get it sorted out.

        BTW, a good technique that may save you a lot of grief is to comment out you rmtree code and use a print instead as a trial run to see what is going to be deleted when you reinstate the rmtree and run again.

        True laziness is hard work
Re: Syntax Error deleting files and folders in a directory
by GrandFather (Saint) on Dec 28, 2013 at 22:04 UTC

    You have stuck strictures (use strict; use warnings;) at the start of the script you have shown us, but most of the errors NetWallah describes in Re: Syntax Error deleting files and folders in a directory would be picked up by strictures but are masked by an error you don't describe:

    Possible attempt to separate words with commas at noname.pl line 4. "make_path," is not exported by the File::Path module Can't continue after import errors at noname.pl line 4. BEGIN failed--compilation aborted at noname.pl line 4.

    After fixing the first error flagged by strictures we get:

    Parentheses missing around "my" list at noname.pl line 16. "my" variable @files masks earlier declaration in same scope at noname +.pl line 20. Global symbol "$deleletes" requires explicit package name at noname.pl + line 20. Execution of noname.pl aborted due to compilation errors.

    I guess the first of those errors is the one you are having trouble with, but it's a little hard to tell because it's not on the line you describe and you don't tell us what the actual error is. However strictures picked up four out of five serious errors that NetWallah reported. tobyink reported another error and it is very likely that:

    my $destinationDir = 'C:\Users\Shaun\Documents\$website';

    which assigns a path string ending in '$website' (not the contents of the Perl variable $website) to the variable $destinationDir is probably not what you intended, but given the absence of $website elsewhere in the script it's hard to be sure.

    So a bunch of lessons here:

    1. Always use strictures (use strict; use warnings; - see The strictures, according to Seuss).
    2. Fix first things first.
    3. When asking for help provide all the relevant information you have.

    Oh, and as an aside: don't use tabs for indentation. Convince Eclipse (with a blunt instrument if need be) to insert spaces instead of tabs. Your indentation is fine, but the tab characters screwed up what NetWallah saw.

    True laziness is hard work
Re: Syntax Error deleting files and folders in a directory
by kcott (Archbishop) on Dec 28, 2013 at 22:33 UTC

    G'day smturner1,

    Welcome to the monastery.

    While you're learning, you'll find it useful to use the diagnostics pragma, i.e.

    ... use strict; use warnings; use diagnostics; ...

    This will provide more detailed explanations of the normally much shorter warning and error messages you receive. This is a developer tool that produces a lot of output that's typically unsuitable for endusers: it's generally a good idea to either delete or comment out the use diagnostics; line in your production code. Follow the link I provided for a more complete description.

    In addition to the already lengthy list of issues, it looks like you may have another problem with:

    my $destinationDir = 'C:\Users\Shaun\Documents\$website';

    unless the $destinationDir path does in fact end with a directory that's literally called "$website".

    As you haven't declared the $website variable, that could be an omission or that could be exactly what you intended. Regardless, I'll point out some issues that might at least be useful for future reference.

    Variables do not interpolate within single quotes, so $website will evaluate exactly to the string "$website" and not to whatever value was assigned to $website.

    Simply changing the single-quotes to double-quotes does not fix this situation. Every character preceded by a backslash ('\') is considered to be escaped (i.e. it has some special meaning). "C:\Users\Shaun\Documents\$website" will be parsed as follows:

    • \U - no character will be printed for this but all remaining characters will be converted to uppercase (see uc).
    • \S - this produces the warning "Unrecognized escape \S passed through at ..."
    • \D - this produces the warning "Unrecognized escape \D passed through at ..."
    • \$ - this produces a literal '$' and the following characters 'website' will not be considered part of the $website variable but will be used verbatim.

    All of this means that "C:\Users\Shaun\Documents\$website" will evaluate to:

    C:SERSSHAUNDOCUMENTS$WEBSITE

    To fix this, you'd need to escape the escapes, i.e. change each instance of '\' to '\\':

    "C:\\Users\\Shaun\\Documents\\$website"

    Clearly, this is now becoming rather messy, less readable and error-prone.

    A better way to deal with this (from coding, maintenance and portability perspectives) is to use the core module File::Spec. In this specific instance, you could simply write:

    File::Spec->catfile('C:\Users\Shaun\Documents', $website)

    [Aside: When posting questions about error messages you receive, we can provide much better help if you post the exact error message rather than a rough description of it. Details about this, as well as other guidelines for posting here, can be found in "How do I post a question effectively?".]

    -- Ken

      Thank you Ken. MS is the problem here, regarding the '\'. I knew that it would probably become an issue due to the fact that it is used to escape chars to literals and not syntax. I will research File::Spec as I was informed by my Perl sme that there was a module that could correct MS's ridiculous '\' issue within directory names, but he could not remember the name of the module. My suspicion is that you just provided me the answer/module I was looking for. As for the error; Padwalker via EPIC only gave me "Syntax Error" on the line I cited. I apologize for being vague, but I had no other information. I will also employ the diagnostics module so that I can avoid the same in the future. I appreciate this community so much now that I have found it. What a prize to have such a supportive group, especially for a Newb.

        Nah, MS is not the problem. Pretty much anywhere you can use / instead of \ in MS paths (some applications get grumpy, but it's pretty much always ok in Perl).

        You may find Padre IDE better than Eclipse/EPIC for working with Perl scripts. It's a long while since I last looked at the Eclipse/EPIC abomination and it may have improved out of sight, but Eclipse was a poor fit for use as a Perl IDE. If all you need is a good editor take a look at Komodo Edit or Sublime Text. The Komodo IDE is excellent, but costs around $US300 so unless you plan to be a serious user it's likely not an option.

        In any case running the script should get you the real diagnostics that Eclipse/EPIC is protecting you from.

        True laziness is hard work
Re: Syntax Error deleting files and folders in a directory
by Anonymous Monk on Dec 29, 2013 at 04:27 UTC