You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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会基于指定的远程源目录匹配文件),但进程替换的执行位置错误才是根本问题。

解决方法

方法一:远程生成临时文件列表再同步

  1. 先通过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");
  1. 调用rsync_get,指定远程临时文件作为文件列表来源:
$ssh->rsync_get({exclude => '*~', archive => 1, safe_links => 1, verbose => 1,
                 files_from => $remote_list}, 
                '/var/spool/dir/quarantine/', '/home/phil/quarantine/');
  1. 同步完成后删除远程临时文件:
$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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 11:24:23