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

After much cb chatter, no one could come up with a good, portable, solution to copying directories. Preferably in pure perl. Anyone got a good one?

Replies are listed 'Best First'.
Re: Copying a directory recursively
by arden (Curate) on Mar 10, 2004 at 01:25 UTC
    Have you looked at File::DirSync yet? It hasn't been updated in about eight months, but it looks like it should do the trick.

    - - arden.

      File::Repl is very similar and I have used it a few times. Looking over the examples, File::Repl might be more powerful, and I'm not sure which is actually faster -- but Repl does remind me a lot of tools like the kick-butt rsync, so at least it gives me the warm fuzzies*. I'm considering using File::Repl to mirror my ogg-vorbis directory to my new audio player -- maybe with an X::Osd progress bar :)

      * = mooses are fuzzy and warm anyway, it does not take much.

Re: Copying a directory recursively
by broquaint (Abbot) on Mar 10, 2004 at 02:34 UTC
    With a bit of help from the File:: modules I've come up with this code snippet
    use strict; use warnings; use File::Basename 'basename'; use File::Find::Rule 'find'; use File::Path 'mkpath'; use File::Copy 'copy'; use File::Spec::Functions qw/ splitdir catdir splitpath catfile /; $::PROG = basename $0; my($orig, $new) = @ARGV; die "Usage: $::PROG SRC DEST\n" unless defined $new and defined $orig; my $iter = find start => $orig; while(my $ent = $iter->match) { if(-d $ent) { my $mode = ( stat $ent )[2]; my @src = splitdir $ent; my $dest = catdir $new => @src; mkpath $dest => 0, $mode or die "$::PROG: Couldn't create '$ent': $!"; } else { my @src = splitpath $ent; my $dest = catfile $new => @src; copy $ent => $dest or die "$::PROG: Couldn't copy '$ent': $!"; } }
    Not having a plethora of systems to work with, I couldn't say how portable that is, but given the ubiquity of File::Spec it should be relatively reliable. However, I'd say in this case it'd probably be simpler just to have a switch-esque statement to call the appropriate system copying tool per system to get reliable and consistent recursive copying.
    HTH

    _________
    broquaint

      Be wary of the File::Find module, it has a memory leak. See File::Find memory leak I tend to avoid it now.

      Is there a Win32 module similar to File::Dirsync or File::Repl mentioned above? My search on ppm3 didn't find anything. I'm looking for a similar solution myself. It my searches prove unsuccessful I'll end up writing my own, just researching now.

      Thanks
      Dean

      Programming these days takes more than a lone avenger with a compiler. - sam
      A Standard for the Transmission of IP Datagrams on Avian Carriers
        File::Repl works on Win32. ppm probably doesn't have it, but IIRC you can install from CPAN without needing a compiler.

        perl -MCPAN -e "install File::Repl".

        I would try that -- if you have problems, you may need to download a free copy of nmake from Microsoft (and possibly rename it make) to get CPAN to work, but other than that, it should be fine. Again, I don't think a compiler is required. We use this module at Work on Win32 quite often in our build system. No problems yet!

Re: Copying a directory recursively
by Abigail-II (Bishop) on Mar 10, 2004 at 02:29 UTC
    How portable do you want it to be? Portable between any POSIX system? How should it behave when encountering non-files, like fifos or symbolic links? How do you want permissions and ownership be handled?

    I'd go for a non-Perl solution, and choice any off:

    • cp -R or cp -a
    • tar cf - src | (cd dest; tar xf -)
    • cpio
    • rsync
    I've copied directories zillions of times, but never with a pure Perl solution.

    Abigail

Re: Copying a directory recursively
by bl0rf (Pilgrim) on Mar 10, 2004 at 02:05 UTC
    I raced off to program a solution in Perl, trying to beat everyone else. The result follows, although it doesn't handle binary and Only now I've noticed that you wanted recursion, but that is fairly easy to add on.
    #!usr/bin/perl -W # pure Perl directory copying script use File::Find; $from = $ARGV[0]; # from should be full path $to = $ARGV[1]; # to should have / at the end mkdir( $to ) || die "cant mkdir $to: $!"; find( \&copyroutine, $from ); sub copyroutine { if( $_ eq '.' || $_ eq '..' ){ return 1 } print "got $_\n"; open( FROM, $_ ) || die "cant open $_: $!"; open( TO, "> $to$_" ) || die "cant make $to$_ : $!"; while( $line = <FROM> ) { print TO $line; } close TO; close FROM; }

    I know just how much everyone likes to perfect solutions, and add alternate ones - so I leave it to others to implement recursion and binary handling. The above code works with a flat directory on my trusty 333Mhz windows box.

    The guy with the awful signature

Re: Copying a directory recursively
by TomDLux (Vicar) on Mar 10, 2004 at 01:50 UTC

    Who needs a Perl solution when rsync works on all significant platforms? ... even between platforms!

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

      Last I heard, there's not a working rsync for VMS...yet.
Re: Copying a directory recursively
by thunders (Priest) on Mar 10, 2004 at 03:30 UTC
    Have you tried File::NCopy it appears to do what you need. I've used it in the past on Win32 with some success. Also the core modules File::Find and File::Copy together should do most of what you need.
Re: Copying a directory recursively
by Happy-the-monk (Canon) on Mar 10, 2004 at 01:34 UTC

    Just a non-perlish train of thought...

    <!perl>   tar cf - source | ( cd target ; tar xf - )   </!perl>

    that's my unox recursive copying incantation.
    But how would it translate to Perl?

    it seems Archive::Tar only deals with file archives at first sight.
    But it might be twisted to work as the tar loop, or not? And what about PAR?

Re: Copying a directory recursively
by zentara (Cardinal) on Mar 10, 2004 at 16:49 UTC
    This is the easiest I've come across:
    #!/usr/bin/perl use File::NCopy; $file = File::NCopy->new(recursive => 1); $file->copy($dir1, $dir2); # Copy $dir1 to $dir2 recursively

    I'm not really a human, but I play one on earth. flash japh
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Copying a directory recursively
by Plankton (Vicar) on Mar 10, 2004 at 05:08 UTC
    I am assuming that when you say "pure perl" you mean without having to use a Perl module. In that case why not case off of the $^O (OS) variable ... you know ... something like this ...
    sub setRecursiveCopyCommand { my $OS = $^O; if ( $OS eq 'VMS' ) { return whatever the hell they do in VMS } elsif ( $OS eq 'PLAN9' ) { return whatever the hell they do in PLAN9 } elsif ( $OS eq monkeys_fly_out_of_my_ass ) { you get the picture } else { ... }
    Note above code is untested. And may require some effort on your part ... :P

    Plankton: 1% Evil, 99% Hot Gas.
      Because trying to shell out to xcopy wasn't working =/. Actually by "pure perl" I meant not utilizing any other binary.

        Note that by default DOS's xcopy does not copy recursively, you have to give a switch to do that, /E I think.

Re: Copying a directory recursively
by Anonymous Monk on Mar 10, 2004 at 04:52 UTC
    Sorry this is not a Perl solution but this solution should be portable to all *nix OS's and Windows OS that cygwin installed on them ...
    $ cp -r src/* tgt