如何在 perl 脚本中从 ftp 复制最新的

How do I copy the latest from ftp in a perl script

提问人:Zilore Mumba 提问时间:10/19/2023 最后编辑:zdimZilore Mumba 更新时间:11/1/2023 访问量:106

问:

我有一个 perl 脚本,它从 ftp 服务器上的目录中复制一个文件。文件名的格式为 FPZB01_00000118.txt。.txt 之前的数字每天递增 1。远程服务器上每天应该只有一个文件。如果有 1 个以上的文件,脚本将复制所有文件,这是不希望的。我正在尝试添加代码以仅复制今天的文件,但卡住了。 代码如下,省略了 use::strict、...和其他定义。

my $ftp = Net::FTP->new( $host, Timeout => 360, Passive => 1, Debug=>1 ) or die "Error connecting to $host: $!\n";
$ftp->binary();
$ftp->login($User,$Pass) or die "Cannot login to $User\n";
$ftp->cwd($remote_dir) or die "Could not change remote working directory to  $remote_dir\n";
my @remote_files = $ftp->ls($glob);

添加代码

my @file2copy = grep /^FPZB01/, $ftp->ls();
foreach my $fl (@file2copy){
 my $mdtime = $ftp->mdtm($fl);
 print $f "modtime $mdtime\n";
 exit;
}

结束 添加的代码

foreach my $file (@remote_files) {

 # Transfer file
 print "Getting $file\n" if $debug;
 eval {$ftp->get($file, "$local_dir/$file") or print("Can't send file $file\n")};
 if ($@ =~ /Timeout/){
  print "Got a timeout Issue: $@";
 }     
}

#   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
print "copying ends\n";
 exit 0;

我试图打印修改; 这是我得到的:print $f "modtime $mdtime\n"

modtime 1697627482. 

不知道这意味着什么。 有关如何使用的帮助将不胜感激$ftp->mdtm($fl)

文件 日期时间 perl 时间戳

评论

0赞 Steffen Ullrich 10/19/2023
“不知道这意味着什么。”- 看起来像 UNIX 时间,即自 70/01/01 以来的秒数。但总的来说 - 更高的数字意味着更新,因此您可以通过比较轻松找出最新文件是什么。
0赞 Zilore Mumba 10/19/2023
谢谢@Ullrich ,自 1970 年 1 月 1 日以来确实是几秒钟。我会试着从那里锻炼。

答:

2赞 Dave Cross 10/19/2023 #1

只需对文件列表进行排序并传输数组中的最后一个文件即可。

# Use grep to ignore any files with the wrong name
my @remote_files = grep { /^FPZB/ } sort $ftp->ls($glob);

my $file_to_transfer = $remote_files[-1];

评论

0赞 Zilore Mumba 10/19/2023
感谢 @Dave Corss,我也会尝试您的解决方案。顺便说一句,这里重要的是修改时间,而不是名称。
0赞 Dave Cross 10/20/2023
@ZiloreMumba:但是你说名字末尾的数字每天都在递增。因此,您可以忽略修改时间,仅使用文件名。
0赞 Zilore Mumba 10/21/2023
再次感谢@Dave,这有效,但显然是 $remote_files[-1];给出最旧的文件,这没关系,因为要获取最新的文件,我只需获取 $remote_files[0];。我很感激。至于下面的其他代码,我在系统上处理 epoch 这个词时遇到了问题。
0赞 Dave Cross 10/22/2023
@ZiloreMumba:如果文件名末尾的数字每天都在增加,并且您正在运行我编写的代码,那么必须是最新的文件。如果不是这种情况,那么还有其他事情没有告诉我们。$remote_files[-1]
2赞 Zilore Mumba 10/19/2023 #2

这是我为使其工作而添加的内容 我有时间 12 小时前

my $TodayDate = strftime "%d.%m.%Y  %H:%M:%S", localtime(time - 12*60*60);

登录后,计算我 12 小时前日期的纪元时间

my ($mday,$mon,$year,$hour,$min,$sec) = split(/[\s.:]+/, $TodayDate);
my $time = timelocal($sec,$min,$hour,$mday,$mon-1,$year);

然后,我循环浏览文件并仅查找比 12 小时前更新的文件

    my @file2copy = grep /^FPZB01/, $ftp->ls();
foreach my $fl (@file2copy){
 my $mdtime = $ftp->mdtm($fl);
 if($mdtime > $time){
  #Transfer file

其余代码保持不变,传输文件。这并不优雅,但它符合我的意愿。 在序言中添加

 use strict;
 use warnings;
use POSIX qw{strftime};
use Time::Local
use Time::Piece;
use Net::FTP;
use Net::SFTP::Foreign;

感谢 Ullrich 和 Toto 如何将文本日期转换为时间戳?

评论

1赞 Dave Cross 10/20/2023
但是你说“.txt 之前的数字每天递增 1”。因此,关于修改时间的整个事情是毫无意义的。您只需对文件名进行排序,然后选择数量最多的文件名。
2赞 zdim 10/20/2023 #3

我不确定文件选择的确切标准是什么,因为这个问题似乎与一些 OP 的评论略有不同,也许也与 OP 自己的回答略有不同。以下是一些选项。


超过 12 小时被用作 OP 答案的标准。这可以用于

my @file2copy = grep { -M < 0.5 } $ftp->ls();

由于 filetest (-X) 给出-M

-M 脚本开始时间减去文件修改时间,以天为单位。

如果需要,也可以对其进行排序(见下文)以获取最新信息。


在 OP 的评论中提到了修改时间,例如可以使用什么

use Sort::Key qw(rukeysort);  # Reverse (descending) sort as Unsigned integers

my @files_latest_mtime = rukeysort { (stat $_)[9] } $ftp->ls();

当需要更复杂的排序标准时,Sort::Key 库函数非常方便,因为可以直接使用比较标准(并且在内部它使用 Schwartzian 变换以提高效率)。我使用 stat 来获取修改时间(自纪元以来的秒数);然后更大的时间用于较新的文件,因此我们需要“反向”排序(降序)。


人们还可以使用日期时间库,并从时间戳中构建 modtime 对象,然后可以随心所欲地使用。使用 DateTime

use DateTime;
use Sort::Key qw(rukeysort);

# Build an array with [filename,dt] arrayrefs, newest files first
my @files_dt_latest =
    # grep if needed
    rukeysort { $_->[1]->epoch }
    map { [$_, DateTime->from_epoch(epoch => (stat $_)[9])] } 
        $ftp->ls();

say "$_->[0] => $_->[1]" for @files_dt_latest;

其中返回文件自纪元以来的年龄(以秒为单位),方便排序。或者使用哈希epoch

my %file_dt = 
    map { $_ => DateTime->from_epoch(epoch => (stat $_)[9])] } 
        $ftp->ls();

这可以根据需要进行操作

foreach my $file (rukeysort { $file_dt{$_}->epoch } keys %file_dt) { 
    say "$file => $file_dt{$file}"  # newest first
}

或者使用库,这里 ,获取今天的日期,比如说自纪元以来的秒数,以便于与文件的时间戳进行比较。若要仅获取今天的文件,请执行以下操作:DateTime

use DateTime;
use Sort::Key qw(rukeysort);

# "today" returns today's date at 00:00:00, in UTC by default
my $today_epoch = DateTime->today(time_zone => 'local')->epoch;

my @files_today_latest_by_mtime = 
    rukeysort { (stat $_)[9] } 
    grep { (stat $_)[9] > $today_epoch } 
        $ftp->ls();

这也许可以针对性能(或风格?)进行优化,因为反复使用,但我怀疑人们是否能分辨出其中的区别。而“更好”的版本会更复杂。stat

评论

0赞 Zilore Mumba 10/20/2023
这很有趣。我会尝试并提供反馈