使用Perl Net::OpenSSH的rsync_get()基于ctime同步远程文件失败求助
基于远程文件ctime用rsync同步文件的问题与解决
我尝试在Perl脚本中,借助rsync基于远程服务器上某文件的ctime,从远程目录同步文件,但遇到了困难。
脚本代码
use strict; use Net::OpenSSH; my $user = "phil"; my $password = 'pass'; my $host = "xavier"; my $local_rules = "/etc/mail/rules.cf"; my $remote_rules = "/etc/mail/rules.cf"; my $keypath = "$userhome/.ssh/scp-key.key"; my $ssh = Net::OpenSSH->new($host, user => $user, passphrase => $password, key_path => $keypath); $ssh->rsync_get({exclude => '*~', archive => 1, safe_links => 1, verbose => 1, files_from => "<(find /var/spool/dir/quarantine -cnewer /home/phil/last-run -type f -exec basename {} \\;)>"}, '/var/spool/dir/quarantine/','/home/phil/quarantine/');
执行错误信息
rsync: failed to open files-from file <(find /var/spool/dir/quarantine -cnewer /home/phil/last-run -type f -exec basename {} \;)>': No such file or directory rsync error: syntax or usage error (code 1) at main.c(1765) [client=3.2.4]
手动在远程服务器执行该find命令时,能正常生成文件列表。我原本以为find命令会在远程主机执行,但可能并非如此?请问我忽略了什么?有没有更好的实现方式?
问题原因
你这里的核心问题是:files_from参数里的<(...)是bash的进程替换语法,但Net::OpenSSH的rsync_get方法不会将这个语法传递到远程主机执行——实际上rsync客户端会在本地尝试打开这个"文件",而本地不存在这个进程替换生成的管道,因此报错找不到文件。
另外,就算进程替换能生效,用basename只取文件名的操作本身没问题(rsync会基于指定的远程源目录匹配文件),但进程替换的执行位置错误才是根本问题。
解决方法
方法一:远程生成临时文件列表再同步
- 先通过Net::OpenSSH在远程执行find命令,将结果保存到远程临时文件:
my $remote_list = "/tmp/rsync_files.list"; # 用sed去掉路径前缀,让列表中的路径相对于远程源目录 $ssh->system("find /var/spool/dir/quarantine -cnewer /home/phil/last-run -type f | sed 's|^/var/spool/dir/quarantine/||' > $remote_list");
- 调用rsync_get,指定远程临时文件作为文件列表来源:
$ssh->rsync_get({exclude => '*~', archive => 1, safe_links => 1, verbose => 1, files_from => $remote_list}, '/var/spool/dir/quarantine/', '/home/phil/quarantine/');
- 同步完成后删除远程临时文件:
$ssh->system("rm -f $remote_list");
方法二:通过SSH管道传递文件列表(无需临时文件)
直接在本地构造rsync命令,通过SSH将远程find的结果传递给本地rsync,利用rsync的--files-from=-从标准输入读取文件列表:
my $rsync_cmd = sprintf( 'ssh %s@%s "find /var/spool/dir/quarantine -cnewer /home/phil/last-run -type f | sed \'s|^/var/spool/dir/quarantine/||\'" | rsync -av --exclude="*~" --safe-links --files-from=- %s@%s:/var/spool/dir/quarantine/ /home/phil/quarantine/', $user, $host, $user, $host ); system($rsync_cmd);
注意事项
- 确保远程用户
phil对/var/spool/dir/quarantine有读取权限,对/tmp目录有写入权限(方法一需要)。 - 两种方法中的sed命令都是为了去掉绝对路径前缀,避免rsync错误地从远程根目录同步文件。
内容的提问来源于stack exchange,提问作者Alex Regan
相关产品推荐
相关产品推荐

