Listing 4: mirror_get
Recursive Traversal of an FTP Site
The Perl Journal, Winter 1999
 

Listing 4: mirror_get



#!/usr/bin/perl -w
# Transfers remote file tree to the local machine.
#     usage: mirror_get [-d -D] host remotedir name password
#        -d  Debug mode - don't actually transfer anything.
#        -D  Turn on Net::FTP debug messages

use Getopt::Std;
use Net::FTP;
use FTPFind;
use Cwd;
use File::Path;

getopts("dD");
defined($opt_d) or $opt_d = 0;
defined($opt_D) or $opt_D = 0;

my $host     = shift;
my $dir      = shift;
my $name     = shift;
my $password = shift;

defined($host) and 
  defined($dir)  and 
  defined($name) and 
  defined($password) or 
die "usage: $0 [-D] host remotedir name password";

$ftp = Net::FTP->new($host, Debug => $opt_D ? 1 : 0)
    or die "ERROR: Net::FTP->new() failed\n"; 

$ftp->login($name, $password) or die "ERROR: login() failed\n";

# Assume binary transfers.
$ftp->binary;

# Go to the starting point in the remote tree.
$ftp->cwd($dir) or die "ERROR: can't cwd() on $dir\n";

my $root = cwd;   # Remember local root directory.

print "DIRECTORY: ", $ftp->pwd, "\n";

ftp_finddepth($ftp, \&process_item, 1);    # Crawl over the tree

$ftp->quit;

sub process_item {
    my ($level, $isdir, $item, $parent) = @_;

    foreach (1..$level) { print "   " }
    if ($isdir) {
        print "DIRECTORY: ",$parent,"/",$item,"\n";
        if (!$opt_d) {
            # Prepend the remote path with a . and hang it
            # off the directory where we started.
            my $path = "." . $parent . "/" . $item;
            chdir($root)  or die "ERROR: can't chdir() to $root\n";
            mkpath($path) or die "ERROR: can't mkpath() $path\n";
        }
    } else {
        print "FILE: ", $item, "\n";
        if (!$opt_d) {
            chdir($root) or die "ERROR: can't chdir() to $root\n";
            chdir("." . $parent) or 
die "ERROR: can't chdir() to .$parent\n";
            $ftp->get($item) or die "ERROR: get() failed on $item\n";
        }
    }    
}