René Nyffenegger's collection of things on the web | |
René Nyffenegger on Oracle - Most wanted - Feedback
- Follow @renenyffenegger
|
ftp diff [PERL script] | ||
ftp_diff.pl
use strict; use warnings; use NET::FTP; my $host = $ARGV[0]; my $user = $ARGV[1]; my $pass = $ARGV[2]; my $ftp_dir_top = $ARGV[3]; my $local_dir_top = $ARGV[4]; my $ftp = Net::FTP -> new ($host) or die 'could not establish ftp connection'; $ftp -> login($user, $pass) or die 'login failed'; my @cur_path = (); diff_dir(\@cur_path); sub diff_dir { my $cur_path_ref = shift; my $path_relative = '/' . (join '/', map {$_->{dir}} @{$cur_path_ref}) . '/'; my $local_dir = $local_dir_top.$path_relative; my $ftp_dir = $ftp_dir_top.$path_relative; die "not a dir $local_dir" unless -d $local_dir; opendir DIR, $local_dir or die "could not open $local_dir"; my @dir_list_local_ = sort readdir DIR; closedir DIR; my @dir_list_local; for my $local_dir_or_file (@dir_list_local_) { if (-d "$local_dir/$local_dir_or_file") { push @dir_list_local, {name => $local_dir_or_file, type=>'d'}; } elsif (-f "$local_dir/$local_dir_or_file") { push @dir_list_local, {name => $local_dir_or_file, type=>'f'}; } else { die "$local_dir_or_file seems to be neither file nor directory"; } } $ftp -> cwd ($ftp_dir_top . $path_relative) or die "ftp: could not cd to $ftp_dir_top - $path_relative"; $ftp -> cwd ($ftp_dir) or die "ftp: could not cd to $ftp_dir"; my @dir_list_ftp_ = $ftp -> dir; my @dir_list_ftp; for my $ftp_dir_or_file (@dir_list_ftp_) { # Assumption: $ftp_dir_or_file looks like # drwxr-xr-x 2 user group 4096 Sep 28 21:15 dir_d_sub_1 # or # -rw-r--r-- 1 user group 18 Sep 28 21:51 one.txt my $name = (split ' ', $ftp_dir_or_file)[8]; my $type = substr($ftp_dir_or_file, 0, 1); if ($type eq 'd') { push @dir_list_ftp, {name => $name, type=>'d'}; } elsif ($type eq '-') { push @dir_list_ftp, {name => $name, type=>'f'}; } else { die "$ftp_dir_or_file neither 'd' nor '-'"; } } my $cur_file_local; my $cur_file_ftp; $cur_file_local = next_local_file_or_dir(\@dir_list_local); $cur_file_ftp = next_ftp_file_or_dir (\@dir_list_ftp ); while (defined $cur_file_ftp or defined $cur_file_local) { if (defined $cur_file_ftp and defined $cur_file_local) { if ($cur_file_ftp->{name} eq $cur_file_local->{name}) { if ($cur_file_ftp->{type} eq 'd' and $cur_file_local->{type} eq 'd') { push @{$cur_path_ref}, {dir=>$cur_file_ftp->{name}, already_printed=>0}; diff_dir($cur_path_ref); pop @{$cur_path_ref}; } elsif ($cur_file_ftp->{type} ne $cur_file_local->{type}) { print_path_and_text("$cur_file_ftp->{name}: ftp has type $cur_file_ftp->{type}, local has type $cur_file_local->{type}", $cur_path_ref); } $cur_file_local = next_local_file_or_dir(\@dir_list_local); $cur_file_ftp = next_ftp_file_or_dir (\@dir_list_ftp ); } elsif ($cur_file_ftp->{name} lt $cur_file_local->{name}) { missing($cur_file_ftp , $cur_path_ref, 'local', \$cur_file_ftp, \$cur_file_local, \@dir_list_ftp, \@dir_list_local); } else { # $cur_file_ftp gt $cur_file_local missing($cur_file_local, $cur_path_ref, 'ftp', \$cur_file_ftp, \$cur_file_local, \@dir_list_ftp, \@dir_list_local); } } elsif (defined $cur_file_ftp and not defined $cur_file_local) { missing($cur_file_ftp , $cur_path_ref, 'local', \$cur_file_ftp, \$cur_file_local, \@dir_list_ftp, \@dir_list_local); } elsif (not defined $cur_file_ftp and defined $cur_file_local) { missing($cur_file_local, $cur_path_ref, 'ftp', \$cur_file_ftp, \$cur_file_local, \@dir_list_ftp, \@dir_list_local); } else { die "should not have happened..."; } } } sub print_path_and_text { my $text = shift; my $cur_path_ref = shift; my $indent=-1; for my $path_elem (@$cur_path_ref) { $indent ++; next if $path_elem->{already_printed}; print ((" " x $indent). $path_elem->{dir} . " [$indent]\n"); $path_elem->{already_printed} = 1; } print ((" " x ($indent+1)). "$text\n"); } sub missing { my $file = shift; my $cur_path_ref = shift; my $where = shift; my $ftp_file_ref = shift; my $local_file_ref = shift; my $ftp_list_ref = shift; my $local_list_ref = shift; print_path_and_text("$file->{name} missing $where", $cur_path_ref); if ($where eq 'ftp') { $$local_file_ref = next_local_file_or_dir($local_list_ref); } else { $$ftp_file_ref = next_ftp_file_or_dir($ftp_list_ref); } } sub next_local_file_or_dir { my $local_list_ref = shift; my $ret; while (1) { if (@$local_list_ref) { my $file_or_dir_name_ = shift @$local_list_ref; next if $file_or_dir_name_ -> {name} eq '.' or $file_or_dir_name_ -> {name} eq '..'; $ret = $file_or_dir_name_; last; } else { undef $ret; last; } } return $ret; } sub next_ftp_file_or_dir { my $ftp_list_ref = shift; my $ret; while (1) { if (@$ftp_list_ref) { my $file_or_dir_name_ = shift @$ftp_list_ref; next if $file_or_dir_name_ -> {name} eq '.' or $file_or_dir_name_ -> {name} eq '..'; $ret = $file_or_dir_name_; last; } else { undef $ret; last; } } return $ret; }
The script is called with something like:
perl ftp_diff.pl ftp.server.tld username password /htdocs c:/dev/htdocs
where /htdocs specifies the top directory on the ftp server and c:/dev/htdocs the local top directory.
Testcaseftp_diff_testcase.pl
use warnings; use strict; use NET::FTP; use File::Path; use File::Copy; my $host = $ARGV[0]; my $user = $ARGV[1]; my $pass = $ARGV[2]; my $ftp_dir_top = $ARGV[3]; my $local_dir_top = $ARGV[4]; ## logging into ftp server... my $ftp = Net::FTP -> new ($host) or die 'could not establish ftp connection'; $ftp -> login($user, $pass) or die 'login failed'; ## Clean up previous run... $ftp -> rmdir ($ftp_dir_top, 1); rmtree($local_dir_top); ## $ftp -> mkdir ($ftp_dir_top); mkpath ($local_dir_top); $ftp -> mkdir ($ftp_dir_top . "/dir_a"); mkpath ($local_dir_top . "/dir_a"); $ftp -> mkdir ($ftp_dir_top . "/dir_b"); $ftp -> mkdir ($ftp_dir_top . "/dir_b/dir_b_sub"); #mkpath ($local_dir_top . "dir_b"); #$ftp -> mkdir ($ftp_dir_top . "dir_c"); mkpath ($local_dir_top . "/dir_c"); mkpath ($local_dir_top . "/dir_c/dir_c_sub"); $ftp -> mkdir ($ftp_dir_top . "/dir_d"); mkpath ($local_dir_top . "/dir_d"); $ftp -> mkdir ($ftp_dir_top . "/dir_d/dir_d_sub_1"); mkpath ($local_dir_top . "/dir_d/dir_d_sub_1"); $ftp -> put ('x', $ftp_dir_top . "/dir_d/dir_d_sub_1/one.txt"); copy ('x', $local_dir_top . "/dir_d/dir_d_sub_1/one.txt"); $ftp -> mkdir ($ftp_dir_top . "/dir_d/dir_d_sub_2"); mkpath ($local_dir_top . "/dir_d/dir_d_sub_2"); $ftp -> put ('x', $ftp_dir_top . "/dir_d/dir_d_sub_2/aaa.txt"); copy ('x', $local_dir_top . "/dir_d/dir_d_sub_2/bbb.txt"); $ftp -> put ('x', $ftp_dir_top . "/dir_d/dir_d_sub_2/ccc.txt"); copy ('x', $local_dir_top . "/dir_d/dir_d_sub_2/ccc.txt"); $ftp -> put ('x', $ftp_dir_top . "/dir_d/dir_d_sub_2/eee.txt"); copy ('x', $local_dir_top . "/dir_d/dir_d_sub_2/ddd.txt"); $ftp -> mkdir ($ftp_dir_top . "/dir_e/"); mkpath ($local_dir_top . "/dir_e/"); $ftp -> mkdir ( $ftp_dir_top . "/dir_e/a_dir_on_ftp_a_file_local"); copy ('x', $local_dir_top . "/dir_e/a_dir_on_ftp_a_file_local"); $ftp -> put ('x', $ftp_dir_top . "/dir_e/a_file_on_ftp_a_dir_local"); mkpath ( $local_dir_top . "/dir_e/a_file_on_ftp_a_dir_local"); system ("perl ftp_diff.pl.raw $host $user $pass $ftp_dir_top $local_dir_top"); |