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

I seek your wisdom PerlMonks. I am attempting to parse
a directory based on "/". After which I plan on using a regex
to capture the number part of the string. Here is an example of a directory:

"/Users/mydirectory/Desktop/BioinfDev/SequenceAssemblyProject/KOMP/11350_Znrf4"
When I run my script I get the error message:
"Global symbol "@fields" requires explicit package name at get_maid_num.pl line 10."
#!/usr/bin/perl use strict; my $komp_dir ='/Users/mydirectory/Desktop/BioinfDev/SequenceAssemblyPr +oject/KOMP/'; my @komp_dir = glob("/Users/mydirectory/Desktop/BioinfDev/SequenceAsse +mblyProject/KOMP/*"); foreach $komp_dir(@komp_dir) { if (-d $komp_dir){ $komp_dir = split /\//; ($komp_dir = $fields[6]) =~ /(^\d*)_(\w*)$/; print "$1\n"; next; } }
I believe that it is how I am doing the regex. Is there a
way to combine the split and regex capture into one
line? If not I still need to capture the $1.
I humbly request the wisdom of the monks.

Replies are listed 'Best First'.
Re: Issue with capturing a string after splitting on "/"
by shmem (Chancellor) on Feb 05, 2009 at 23:15 UTC
    "Global symbol "@fields" requires explicit package name at get_maid_num.pl line 10."

    That happens because you haven't declared the array @fields anywhere.

    foreach $komp_dir(@komp_dir) { if (-d $komp_dir){ $komp_dir = split /\//; # splitting what?

    See split: it operates per default on $_ and returns a list. However, you are assigning the results of split to a scalar, which is also your loop iteration variable.

    ($komp_dir = $fields[6]) =~ /(^\d*)_(\w*)$/;

    No @fields. I guess you meant:

    foreach $komp_dir(@komp_dir) { if (-d $komp_dir){ my @fields = split /\//, $komp_dir; $fields[6] =~ /(^\d*)_(\w*)$/ and print "$1\n"; } }

    You don't need next there, because, as written, there is no statement after the 'next', so there's no need to jump to the beginning of the loop to evaluate the next element - that would happen anyways.

      Thanks Shmem! This will dev into a sub.
Re: Issue with capturing a string after splitting on "/"
by kennethk (Abbot) on Feb 05, 2009 at 23:33 UTC
    1. The error message you are getting means that you never declared a variable named "@fields" which first appears on line 10. I am guessing you intend for that array to be initialized with the results from your split on line 9.
    2. If you use warnings; as well, line 9 spits out a warning because you are attempting to store the array result of a split in a scalar.
    3. Your repeated use of the phrase 'komp_dir' to represent multiple varibles (both scalars and arrays and in multiple scopes) makes reading the code significantly more challenging than it need be.
    4. Good practice says you should make sure a regex matches before attempting to interact with its contents. There's nothing wrong a priori with what you are doing there, though.
    5. Your second parenthetical expression in your regex is never used, so by putting those parentheses in you are creating unnecessary work for the regex engine and potentially adding confusion.
    6. You only need to use next in a loop if you are breaking the natural flow. You are not doing that, so it is unnecessary.

    If you are not interested in anything other than the number component of all subdirectories, I would recommend skipping the splitting all together, and perhaps this would be more of what you need:

    #!/usr/bin/perl use strict; use warnings; my $path ='/Users/mydirectory/Desktop/BioinfDev/SequenceAssemblyProjec +t/KOMP/'; my @komp_dir = glob("$path\*"); foreach my $entry (@komp_dir) { if (-d $entry ){ if ($entry =~ /\/(\d+)_\w*$/) { print "$1\n"; } } }

    or, more succinctly

    #!/usr/bin/perl -w use strict; my $path = '/Users/mydirectory/Desktop/BioinfDev/SequenceAssemblyProje +ct/KOMP/*'; foreach (glob("$path\*")) { print "$1\n" if (-d and /\/(\d+)_\w*$/); }

    As a final note, apparently I type vvvveerrryyy ssssllllooowwwlllllyyyyy....

      Thanks!
Re: Issue with capturing a string after splitting on "/"
by Lawliet (Curate) on Feb 05, 2009 at 23:20 UTC

    Addressing the immediate problem, I would say that you have no array called @fields and therefore cannot access the seventh element of it.

    Delving further, I am confused as to what you are doing. For one, you assign $komp_dir the directory and then never use it. (However, I assume there is more to the program.) Continuing on, let me translate your code to English to see if that is what you want.

    foreach $file in @directory { if the $file is a directory { assign an implicit split to @_ (deprecated) to $file; # hmmm assign the seventh element of @fields to $file and match a reg +ex; # hmmm print the capture from the previous regex; explicitly go to the next element; } }

    I think you want my @path = split(/\//, $komp_dir) instead of your split. I do not really know what you want for the other hmmm'd line. Assuming you want what shmen had written, a simple rindex will suffice. (And you can condense the if statement to one line!) you can use File::Basename:

    use File::Basename; my $fname = basename($path);

    Update: added three final sentences after reading shemn's post.

    Update: rindex method is not so simple, added easier method.

    And you didn't even know bears could type.

      Hi Lawliet!
      Yes, I want what shmen as written. This will likely become
      a subroutine in a program that I am working on. I have to cp files
      to a dir. The beginning of the files and dir share
      the same title, ie 13323. So I have to capture digits at the beginning of the dir name.

Re: Issue with capturing a string after splitting on "/"
by walkingthecow (Friar) on Feb 05, 2009 at 23:27 UTC
    Because you have the line use strict, you will get the error "Global symbol "@fields" requires explicit package name at get_maid_num.pl line 10." if @fields is not defined.

    You can define the array like so:
    1. my @fields;
    2. my @fields=();
    Either 1 or 2 will work.


    Now, if I am right here, look at the code below and see if this works for you:

    #!/usr/bin/perl use strict; my $result; my $komp_dir = "/Users/mydirectory/Desktop/BioinfDev/SequenceAssemblyP +roject/KOMP/"; opendir(DIR, "$komp_dir"); my @FILES= readdir(DIR); close DIR; foreach my $file (@FILES) { if (-d "$komp_dir/$file") { $result = $file; print "$result\n"; } }


    OR
    #!/usr/bin/perl use strict; my $result; my $komp_dir = "/Users/mydirectory/Desktop/BioinfDev/SequenceAssemblyP +roject/KOMP/"; opendir(DIR, "$komp_dir"); my @FILES= readdir(DIR); close DIR; foreach my $file (@FILES) { if (-d "$komp_dir/$file" && $file =~ /\d+_\w+/) { $result = $file; print "$result\n"; } }


    Now you said you only wanted to capture number part, then do this:
    #!/usr/bin/perl use strict; my $result; my $komp_dir = "/Users/mydirectory/Desktop/BioinfDev/SequenceAssemblyP +roject/KOMP/"; opendir(DIR, "$komp_dir"); my @FILES= readdir(DIR); close DIR; foreach my $file (@FILES) { if (-d "$komp_dir/$file" && $file =~ /(\d+)_\w+/) { $result = $1; print "$result\n"; } }
Re: Issue with capturing a string after splitting on "/"
by toolic (Bishop) on Feb 06, 2009 at 02:22 UTC
    Our fellow Monks have provided solutions to your particular problem. Here is yet another way to retrieve the leaf directory (or file) from a long path. It uses the splitdir function from the File::Spec core module. splitdir splits up an OS-independent path specifier, and returns a list. The [-1] just grabs the last element of the list.
    use strict; use warnings; use File::Spec; my $dir = (File::Spec->splitdir('/path/to/dir_i_want'))[-1]; print "$dir\n"; __END__ dir_i_want

      Hi toolic!
      Yes, the monks have provided me with far more than I had expected
      and I am grateful. The use File::Spec saves a lot of time.
      Thanks!
      Lom Space

Re: Issue with capturing a string after splitting on "/"
by didess (Sexton) on Feb 05, 2009 at 23:44 UTC
    Hi ! If your only need is to get the numerical part, I propose this solution :
    #!/usr/bin/perl use strict; my $komp_dir ='/Users/mydirectory/Desktop/BioinfDev/SequenceAssemblyPr +oject/KOMP/'; my @komp_dir_content = glob("$komp_dir/*"); my $k_dir; foreach $k_dir(@komp_dir_content) { if (-d $k_dir){ $k_dir =~ s(.*/)(); next if ( $k_dir !~ /^\d+_/); $k_dir =~ s(_.*)(); print "$k_dir\n"; next; } }
    I hope it helps ! The problem came from the "use strict" and the loop variable being not declared at main level :

    either don't use "use strict" or declare the loop variable (became $k_dir here)

    I changed a little variable-names because I think reusing the same name for different usages is an unuseful risk for programs maintenance.

    But, of course, you're quite free ...

      Hi Didess
      You are right about the variable names, but I miss where the
      digit capture occurs?
      Thanks!
      Lom Space