假设我有一个目录列表,每个目录可能包含也可能不包含我也想考虑的子目录。
还假设我有一个时间戳列表,列表中的每个目录(但不是子目录)都有一个时间戳。这些被称为具有隐式时区的日期和时间,因此可以很容易地转换为 Unix 时间戳,如果这样更容易比较的话。
我如何, 对于列出的每个目录,找出目录中是否存在比我对相关目录的时间戳更新的文件(就 mtime 或 ctime 而言,但不是 atime)
我对哪个特定文件比时间戳更新并不真正感兴趣,只关心是否存在任何此类文件。
基本上, 我想编写一个脚本,如果一组目录中的任何一个目录中的任何文件在给定时间点后发生更改,则在运行时执行特定操作,并且需要想出一种方法来检测是否一切都改变了。
你的问题可以转化为多个简单的子问题
问:如何递归查看目录中的每个文件?
use File::Find
。这看起来有点像
use File::Find;
find sub {
return unless -f;
if (file_is_newer_than($timestamp)) {
do something;
},
}, $top_dir;
问:如何为多个目录执行此操作?
A:将其包装在 foreach 循环中,例如
for my $dir_time (["./foo", 1234567890], ["./bar", 1230987654]) {
my ($top_dir, $timestamp) = @$dir_time;
# above code
}
Q:如何判断文件是否更新?
stat
mtime
或 ctime
,然后将结果与您的时间戳进行比较。例如
use File::stat;
say "$_ is new!" if stat($_)->mtime > $timestamp;
问:我只对是否存在任何此类文件感兴趣。我怎样才能短路
find
?
A:棘手的一个。我们不能只从
return
find
,因为那样只会从我们传递给它的 coderef 中退出。相反,我们可以使用异常控制流反模式:
eval {
find {
wanted => sub {
return unless -f;
die "New file found\n" if stat($_)->mtime > $timestamp;
},
no_chdir => 1,
} $top_dir;
};
if ($@) {
# I should really use exception objects here…
if ($@ eq "New file found\n") {
say "New file in $top_dir found";
} else {
die $@; # rethrow error
}
}
我设置了
no_chdir
选项,这样我就不必在异常处理程序中恢复正确的工作目录。
或者我们可以在标记块上使用循环控制:
DIR: for my $dir_time (...) {
my ($top_dir, $timestamp) = @$dir_time;
RECURSION: {
find {
wanted => sub {
return unless -f;
last RECURSION if stat($_)->mtime > $timestamp; # exit the RECURSION block
},
no_chdir => 1,
} $top_dir;
# if we are here, no newer file was found.
next DIR; # make sure to skip over below code; go to next iteration
}
# this code only reached when a newer file was found
say "New file found";
}
虽然这不会滥用控制流的异常,但这会触发警告:
Exiting subroutine via last
我们可以用
no warnings 'exiting'
.注意:这里的所有代码都未经测试。
这是一种无需任何模块或外部调用的方法:
for my $file (glob "* ./**/* .[!.]*") { # include subdirs & dot files
my $file_time = $^T - 86400 * (-M $file);
print "$file is newer than $timestamp\n" if $file_time >= $timestamp;
}
-M $file
将为您提供“脚本开始时间减去文件修改时间,以天为单位”,其精度足以乘以 86400 以获得“以秒为单位的年龄”。要获取文件的修改时间,请执行该乘法,然后从脚本的开始时间$^T
(又名$BASETIME
,虽然我无法使该变量起作用)中减去结果乘积。
这个例子简单地循环遍历当前目录中的每个项目,并打印它比给定的时间戳更新,当它为真时。