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

Hello, I have not been successful in passing the return value from
my subroutine 'get_directory' to the file::copy function.
I am using Data::Dumper which has enabled me to see
the variables that I am passing to the file::copy function.
I have the files, but the directory that I want to copy them to is wrong.
When I test my sub get_directory:

sub get_directory{ my @dirs = glob("C:\\Documents and Settings\\mydirectory\\Desktop\ +\KOMP\\*"); foreach my $maid_dir(@dirs){ if (-d $maid_dir){ # directory check if ($maid_dir=~m%\d+\s[A-Z]%){ # match the dir name #my $sub_dir = "C:\\Documents and Settings\\mydirector +y\\Desktop\\KOMP\\$maid_dir"; print "$maid_dir\n"; } } } }
It returns a list of the directories that I want to search
for matches with the files that I want to copy to the matching directories.
When I run the &get_directory in my program:
#!/usr/bin/perl use strict; use File::Copy; use File::Glob ':glob'; use Data::Dumper; my %hu_hd_ltvecsmall=(); &cp_to_sequencing_dir; #&get_directory; foreach my $k (keys(%hu_hd_ltvecsmall)){ #get the array from the array reference. my @arr=@{$hu_hd_ltvecsmall{$k}}; #skip unless we have 3 files in the array next unless scalar @arr == 3; my $komp_dir = get_directory($k); print Dumper ($komp_dir); $komp_dir = $komp_dir."\\sequencing"; print Dumper ($komp_dir); #now iterate through the array, copying the files. The maid number i +s ($k) foreach my $f (@arr){ print Dumper($f, $komp_dir); copy("$f","$komp_dir"); } } #=cut sub cp_to_sequencing_dir{ my @dirs = glob("C:\\Documents and Settings\\mydirectory\\Desktop\ +\KOMP\\*"); foreach my $komp_dir(@dirs){ if (-d $komp_dir){ # directory check if ($komp_dir=~m%\d+\s[A-Z]%){ # match the dir name opendir (my $dh, $komp_dir); my @files=readdir $dh; for my $f(@files){ if ($f =~ m/(\d*)(HU|HD|Ltvec_small)/){ # capture +the file name and the int string #print "$1 $f\n"; push @{$hu_hd_ltvecsmall{$1}}, $f; # create + a array reference } } } } } } sub get_directory{ my @dirs = glob("C:\\Documents and Settings\\mydirectory\\Desktop\ +\KOMP\\*"); foreach my $maid_dir(@dirs){ if (-d $maid_dir){ # directory check if ($maid_dir=~m%\d+\s[A-Z]%){ # match the dir name return $maid_dir; } } } }
The second variable being passed to the file::copy function is not
the directory that the files should be copied to.
Example:
$VAR1 = '13129HD.fa' should be copied to $VAR2 = 'C:\Documents and Settings\mydirectory\Desktop\KOMP\13129 Raet1d (46)\sequencing'
It is not iterating through the dir to find the match and
then copy. Any suggestions?

Replies are listed 'Best First'.
Re: Having trouble returning the correct values from my sub
by moritz (Cardinal) on Feb 26, 2009 at 21:30 UTC
    Try to actually return something from your sub. Then you can use the return value.
      I believe that It is returning the list of dirs to search
      but it stops at the second entry. The last two
      entries of the list is where the files should
      be copied. Neither are showing up as the 'copy_to' dir.
Re: Having trouble returning the correct values from my sub
by almut (Canon) on Feb 26, 2009 at 21:54 UTC

    One thing that is weird is that you call the routine with a parameter $k

    my $komp_dir = get_directory($k);

    but you're not using that parameter anywhere in the routine

    sub get_directory{ my @dirs = glob("C:\\Documents and Settings\\mydirectory\\Desktop\ +\KOMP\\*"); foreach my $maid_dir(@dirs){ if (-d $maid_dir){ # directory check if ($maid_dir=~m%\d+\s[A-Z]%){ # match the dir name return $maid_dir; } } } }

    so it will effectively always return the same $maid_dir, i.e. the first directory from @dirs where your regex matches.

    Presumably you intended to use that $k somewhere in get_directory ... (?)

      No I just want the return values

        Then why pass $k?

        Also, you don't get values, you get exactly one value from get_directory(), because as soon as your regex matches, you're leaving the routine (and thus the foreach loop within the routine)...

Re: Having trouble returning the correct values from my sub
by Marshall (Canon) on Feb 27, 2009 at 00:12 UTC
    I have a couple of suggestions:
    First with this code:
    sub get_directory{ my @dirs = glob("C:\\Documents and Settings\\mydirectory\\Desktop\ +\KOMP\\*"); foreach my $maid_dir(@dirs){ if (-d $maid_dir){ # directory check if ($maid_dir=~m%\d+\s[A-Z]%){ # match the dir name return $maid_dir; } } } }
    Point 1: Do not use glob! You could be and probably are heading down the path to what I call "glob hell". I spent a lot of hours there until I figured out how to avoid this place! As it turns out glob() is not portable even amongst Unix platforms. And when you throw Microsoft in there it gets even worse! I know of at least 4 kinds of glob() and they aren't compatible! Bummer! But there is a way...

    Point 2: Perl is designed with Unix style path names, so there is no need to have C:\\dir\\dir2, just use C:/dir/dir2. Perl will translate the / to \ for Windows.

    Point 3: Since we aren't going to use glob() there is no need for *.

    First some code...

    #!/usr/bin/perl -w use strict; my $dir = "C:/Documents and Settings/mydirectory/Desktop/KOMP"; opendir (D, $dir) || die "can't open directory $dir"; my @sub_dirs = grep {!/^\./ && -d "$dir/$_"}readdir D; print join("\n",@sub_dirs),"\n";
    The way to avoid glob() is to open a directory and use grep{} to get what you want. An important part here is that when you use readdir, you only get the name in that directory, NOT the full file path. So you have to add that back in if you do a test on that name. Remember that a directory is just a name, like a file name AND . (the current dir) and .. (the parent directory) will be included in that list! Unix does some special things with files that start with ".", but that doesn't matter here.

    The above code opens a directory. Then it gets a list of sub_directories. The grep{} says: forget any name that starts with a "dot", then test the full path name to see if it is a directory, if so then move it to the output list.

    It is unclear to me what $maid_dir=~m%\d+\sA-Z% is trying to accomplish. That says: directory name has one or more digits somewhere (might not be at the beginning of the name), followed by exactly one white space character, followed by one capital letter and what happens after that doesn't matter. Maybe that's not what you want? Maybe you meant that name must start with a digit? then use /^/ anchor.

    # #example: #@dir_names = get_sub_dirs($path); get_sub_dirs { my $dir = shift; opendir (D, $dir) || die "can't open directory $dir"; my @sub_dirs = grep{/^\d+\s[A-Z]/} grep {!/^\./ && -d "$dir/$_"}readdir D; return (@sub_dirs); }
    The above could be shorter, but you can feed a grep{} into another grep{} this is the same as a logical AND.

      I figured it out. Thanks for the help!