http://www.perlmonks.org?node_id=236548

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

Hello Monks, I'm not an expert in Perl but I'm trying very hard to be one. I'm writing this code to copy "*.rtf" files to another directory. I grap these files with an array and then I tried to copy them to C:\Temp but I don't have any success. This is what I have so far.... I'll appreciate your help.
#! perl -w use strict; use File::Find; use File::Copy; my $line = 'C:\08\00004DC4.013'; opendir MYDIR, $line or die "Could not opendir $line: $!\n"; my @allfiles = grep { $_ ne '.' and $_ ne '..' } readdir MYDIR ; my @files = grep { !-d } @allfiles ; my @dirs = grep { -d } @allfiles ; my @select_files = join($/, grep( /\.rtf$/, @allfiles )); closedir(MYDIR); copy("$File::Find::name","C:\\temp\\/$_"); print "Current directory contains " . @files . " files and " . @dirs . " directories.\n" ; print join("\n", @allfiles);

Replies are listed 'Best First'.
Re: copy files to another directory
by steves (Curate) on Feb 19, 2003 at 06:48 UTC

    A few things to look at:

    1. You don't appear to really be using File::Find. You should already have the files before that using readdir.
    2. When you grep for the *.rtf files, you don't want to join the resulting list. That will flatten it into a delimited scalar.
    3. See the File::Copy docs. The copy method takes two arguments: a source file and a target file. If you follow the suggestions above, you will have a list of files in @select_files. You want to iterate over that and copy each one, something like this:
      foreach my $file (@select_files) { copy($file, "C:\\temp\\$file") or die "Failed to copy $file: $!\n"; }

    I'd put some debug in along the way to see what you have as you try some of these out.

    You can use File::Find something like this:

    use strict; use File::Find; use File::Copy; my $dir = "/tmp"; sub process_file { if ($File::Find::dir ne $dir) { $File::Find::prune = 1; return 0; } return 0 if ($_ !~ /\.rtf$/); copy($File::Find::name, "/tmp/foo/$_") or die "Failed to copy $_: +$!\n"; return 1; } find(\&process_file, $dir);

    Because File::Find does a full directory tree traversal by default (goes into subdirectories) I don't find it as intuitive as just reading the directory with readdir when only looking in one directory. Maybe I'm missing something in the docs that another monk can fill me in on that limits its depth. I usually use the $Find::File::prune = 1 setting as shown above, but I've never been convinced that's the easiest or best way.

      Thanks for your help. Although I've never seen "$Find::File::prune = 1" I understand its use in the code. Thanks again....
Re: copy files to another directory
by Aristotle (Chancellor) on Feb 19, 2003 at 06:49 UTC
    copy("$File::Find::name","C:\\temp\\/$_");

    Where'd $File::Find::name come from? You didn't call File::Find's find(), you read a directory yourself. You're also calling copy() only once, so at best it would copy a single file.

    You correctly use grep to filter the list of all filenames, but a) why use @allfiles there when you aren't interested in directories? and b) you join the list into a single string before assigning it to the @selected_files array, where it gets assigned to the first element. Then you never do something with @selected_files again..

    Something else I noticed: you're opening the directory specified in $line, yet probably confusing the user by saying "Current directory contains [..]".

    #! perl -w use strict; use File::Copy; use File::Spec::Functions qw(catfile); my $line = 'C:\08\00004DC4.013'; opendir MYDIR, $line or die "Could not opendir $line: $!\n"; my @allfiles = grep { $_ ne '.' and $_ ne '..' } readdir MYDIR ; closedir(MYDIR); my @files = grep { !-d } @allfiles ; my @dirs = grep { -d } @allfiles ; print @files." files and ".@dirs." directories in $line\n" ; print map "$_\n", @allfiles; my @select_files = grep /\.rtf\z/i, @files; for my $file (@select_files) { copy catfile($line,$file), catfile("C:\\temp", $file); }

    Makeshifts last the longest.

      I'd rewrite:

      my @files = grep { !-d } @allfiles ; my @dirs = grep { -d } @allfiles ;

      As

      my (@files,@dirs); for( @allfiles ){ if( -d catfile( $line , $_ ) ){ push @dirs,$_ } else { push @files,$_ } }

      Not for cosmetics, but for efficiency and even more to make it work ;)

      --
      http://fruiture.de
        to make it work
        Doh, touché. I didn't even spot that one. That said, I offer
        push @{ -d catfile($line,$_) ? \@dirs : \@files }, $_ for @all_files;

        Makeshifts last the longest.

Re: copy files to another directory
by martymart (Deacon) on Feb 19, 2003 at 10:19 UTC
    Hi, seems a bit complicated for just doing a copy function, why not try, something like this:
    #! usr/bin/perl -w use File::Copy; my @list = <C:\LOCATION\*.rtf>; foreach my $file(@list){ copy ($file,"C:\Temp\"); }
    However, if you wanted to save on the typing you could use a system call (DOS version shown below, I think this'll probably be slower though), you could write this like:
    #! usr/bin/perl -w system ("copy /y LOCATION\\*.rtf C:\Temp");
    this just uses a DOS copy command (the /y is just so that you're not prompted when a file is being overwritten)
    Regards
    Martymart

      I would suggest not doing something like this, for the following reasons:

      #! usr/bin/perl -w # missing a /, although it doesn't matter on DOS use File::Copy; my @list = <C:\LOCATION\*.rtf>; # You seem to be looking for files named 'LOCATION*.rtf', # which probably doesn't exist, putting a backslash in # front of the * makes it a literal *, it isn't a glob # anymore, and I'm not sure what \L will get you, but it # probably won't be the letter L. foreach my $file(@list){ copy ($file,"C:\Temp\"); # \t is a tab, this will fail, unless you actually # have a directory called 'C:(tab)emp', read perlfaq5 # and always use forward slashes, even in dos }

      As for the dos version, it doesn't even really contain any perl, just put the copy command in a batch script.