サーバー間でファイルを双方向に同期する必要が生じたので、perl でスクリプトを書いてみました。
SFTP とは
セキュアなファイル転送プロトコルとしては SFTP と FTPS があって紛らわしいのですが、FTP を SSL/TLS を通して安全に使えるようにしたものが FTPS で、SFTP は FTP とは無関係に、SSH プロトコルの一部(?)としてファイル転送を行えるようにしたもののようです。
scp と sftp と ftps の違いがよく分からん。scp は ssh 経由で cp コマンドを遠隔実行、sftp は ftp とは無関係で ssh 経由で独自のファイル転送プロトコルを実装したもの、ftps はいにしえの ftp を SSL 対応したもの、なのかな。
— Hoshi, Takanori (@hoshi_takanori) April 3, 2014
また、SFTP はスクリプト言語から制御できるので、単純なコピーではなく、双方向の同期にはぴったりだと思われます。
Net::SFTP::Foreign
perl から SFTP を使うには Net::SFTP::Foreign
というモジュールを使えばいいようです。Debian には libnet-sftp-foreign-perl
というパッケージがありました。
$ sudo apt-get install libnet-sftp-foreign-perl
接続
とりあえずこんな感じで接続します。かんたん。
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);
more にいろいろオプションを指定できるようなので、未知の鍵に関していちいち確認されないように StrictHostKeyChecking no
を指定してみました。
ファイル一覧
接続先のファイル一覧は $sftp->ls($dir, ...)
で取得できます。ファイル名が . で始まらない通常ファイルのみをソートして取得してみます。
my $files = $sftp->ls($dir, ordered => 1, no_wanted => qr/^\./,
wanted => sub { S_ISREG($_[1]->{a}->perm) });
結果は配列のリファレンスで、その中身は
foreach my $file (@$files) {
my $filename = $file->{filename};
my $mtime = $file->{a}->mtime;
}
のようにアクセスできます。$file->{a}
は stat の結果と似ています。
ファイルの送受信
ファイルの送信は以下のようにします。
$sftp->put("$local_dir/$filename", "$remote_dir/$filename");
また、ファイルの受信は以下のようにします。
$sftp->get("$remote_dir/$filename", "$local_dir/$filename");
それから、接続先のファイルの削除は以下のようにします。
$sftp->remove("$remote_dir/$filename");
あとは、接続先とローカルのファイルの一覧を比較して、新しい方を送受信すればいいだけですね。削除に関しては、新しい方のファイルサイズが 0 なら削除ということにしてみました。
できあがり
ってなわけで、できあがり。コードは gist にあります。
https://gist.github.com/hoshi-takanori/9948910