Skip to content

Instantly share code, notes, and snippets.

@hoshi-takanori
Created April 3, 2014 05:52
Show Gist options
  • Save hoshi-takanori/9948910 to your computer and use it in GitHub Desktop.
Save hoshi-takanori/9948910 to your computer and use it in GitHub Desktop.
Sync files in local and remote directories via SFTP by perl.
#!/usr/bin/perl
use strict;
use Fcntl ':mode';
use File::stat 'stat';
use Net::SFTP::Foreign;
my $HOST = 'user@example.com';
my $PASS = '********';
my $MORE = [ -o => 'StrictHostKeyChecking no' ];
my $sftp = Net::SFTP::Foreign->new($HOST, password => $PASS, more => $MORE);
sync('local', 'remote');
sub sync {
my ($local_dir, $remote_dir) = @_;
print "sync: $local_dir <-> $remote_dir\n";
my $local_files = local_ls($local_dir);
if (! $local_files) {
print "skip local dir: $local_dir\n";
return;
}
my $remote_files = remote_ls($remote_dir);
if (! $remote_files) {
print "skip remote dir: $remote_dir\n";
return;
}
my %filenames;
foreach my $file (@$remote_files) {
sync_one($local_dir, $remote_dir,
$file->{filename}, $local_files->{$file->{filename}}, $file->{a});
$filenames{$file->{filename}} = 1;
}
foreach my $filename (sort keys %$local_files) {
if (! $filenames{$filename}) {
sync_one($local_dir, $remote_dir,
$filename, $local_files->{$filename}, undef);
}
}
}
sub sync_one {
my ($local_dir, $remote_dir, $filename, $local_stat, $remote_stat) = @_;
my $local_mtime = $local_stat ? $local_stat->mtime : 0;
my $remote_mtime = $remote_stat ? $remote_stat->mtime : 0;
if ($local_mtime == $remote_mtime) {
print "sync_one: skip $filename\n";
if ($local_stat->size != $remote_stat->size) {
print 'SIZE DIFFER: local ', $local_stat->size, ', remote ', $remote_stat->size, "\n";
} elsif ($local_stat->size == 0) {
print "SIZE ZERO\n";
}
} elsif ($local_mtime > $remote_mtime && $local_stat->size > 0) {
print "sync_one: put $filename\n";
$sftp->put("$local_dir/$filename", "$remote_dir/$filename");
print_error();
} elsif ($local_mtime < $remote_mtime && $remote_stat->size > 0) {
print "sync_one: get $filename\n";
$sftp->get("$remote_dir/$filename", "$local_dir/$filename");
print_error();
} else {
if ($local_stat) {
print "sync_one: rm local $filename\n";
unlink "$local_dir/$filename" or print "ERROR: $!\n";
}
if ($remote_stat) {
print "sync_one: rm remote $filename\n";
$sftp->remove("$remote_dir/$filename");
print_error();
}
}
}
sub print_error {
if ($sftp->error) {
print 'ERROR: ', $sftp->error, "\n";
}
}
sub local_ls {
my ($dir) = @_;
opendir DIR, $dir or return undef;
my @filenames = readdir DIR;
closedir DIR;
my $files = {};
foreach my $filename (@filenames) {
my $st = stat "$dir/$filename" if $filename !~ /^\./;
$files->{$filename} = $st if $st && S_ISREG($st->mode);
}
return $files;
}
sub remote_ls {
my ($dir) = @_;
return $sftp->ls($dir, ordered => 1, no_wanted => qr/^\./,
wanted => sub { S_ISREG($_[1]->{a}->perm) });
}
@aims145
Copy link

aims145 commented Nov 17, 2015

Thanks a lot

@MedXAmine
Copy link

thx for the tip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment