我正在创建一个通用编译/转译系统。了解文件是否已编译/转换的一种方法是比较源文件和目标文件的修改日期。
我需要编写一个 bash 脚本来做到这一点:
source_file=foo;
target_file=bar;
stat_source=$(stat source_file);
stat_target=$(stat target_file);
但如何从统计输出中提取日期并进行比较?有没有比stat
比较文件最近修改时间更好的方法呢?
如果我在日志文件上调用 stat,我会得到以下信息:
16777220 12391188 -rw-r--r-- 1 alexamil staff 0 321 "Jun 22 17:45:53 2017" "Jun 22 17:20:51 2017" "Jun 22 17:20:51 2017" "Jun 22 15:40:19 2017" 4096 8 0 test.log
AFAICT,时间粒度不小于秒。如果可能的话,我需要得到比这更细粒度的东西。
答案1
鉴于您正在使用stat
(类似的功能,但 BSD 和 GNU 上的输出格式不同),您还可以使用该test
实用程序,它直接进行比较:
FILE1 -nt FILE2
FILE1 is newer (modification date) than FILE2
FILE1 -ot FILE2
FILE1 is older than FILE2
在你的例子中,
if [ "$source_file" -nt "$target_file" ]
then
printf '%s\n' "$source_file is newer than $target_file"
fi
该功能在 POSIX 中不可用(请参阅其的文档test
),其理由如下:
新发明的或来自 KornShell 的一些附加初选作为条件命令 ([[]]) 的一部分出现在早期提案中: s1
>
s2, s1<
s2, str = pattern, str != pattern, f1-nt
f2, f1-ot
f2 和 f1 -ef f2。当从 shell 中删除条件命令时,它们不会被转移到测试实用程序中,因为它们尚未包含在 sh 实用程序的历史实现中内置的测试实用程序中。
不过,这在未来可能会改变因为该功能得到了广泛支持。
请注意,当操作数是符号链接时,会考虑符号链接目标的修改时间(这通常是您想要的,find -newer
如果不需要,请使用它)。当无法解析符号链接时,实现之间的行为(有些人认为现有文件总是比无法解析的文件更新,有些人总是报告错误的如果任何操作数无法解析)。
另请注意,并非所有实现都支持亚秒粒度(例如,从版本 4.4 开始,bash
's test
/ builtin 仍然不支持,而 GNU和或的内置函数则支持,至少在 GNU/Linux 上)。[
test
test
zsh
ksh93
以供参考:
答案2
在这个linux系统上进行测试。测试文件时间的常用方法是 shell:
[ file1 -nt file2 ] && echo "yes"
似乎可以用秒来完成。这将以小于一秒的时间差接触文件,不会检测到该差异:
$ touch file2; sleep 0.1; touch file1; [ file1 -nt file2 ] && echo "yes"
要确认问题(点后的时间为纳秒):
$ ls --time-style=full-iso -l file?
-rw-r--r-- 1 user user 0 2017-06-23 01:37:01.707387495 -0400 file1
-rw-r--r-- 1 user user 0 2017-06-23 01:37:01.599392538 -0400 file2
file1
比 . 稍微新一些file2
。
现在的问题是如何正确处理时间值。
一种解决方案是使用 ls 的格式化输出:
$ ls --time-style=+%s.%N -l file?
-rw-r--r-- 1 user user 0 1498196221.707387495 file1
-rw-r--r-- 1 user user 0 1498196221.599392538 file2
将时间提取到两个变量(不带点):
$ file1time=$(ls --time-style=+%s%N -l file1 | awk "{print(\$6)}")
$ file2time=$(ls --time-style=+%s%N -l file2 | awk "{print(\$6)}")
并比较时间(纳秒的时间勉强适合 64 位值。如果您的系统不使用 64 位,则此比较将失败):
$ [ $file1time -gt $file2time ] && echo "yes"
yes
这表明它file1
比file2
如果ls
无法获得所需的格式,那么您可以尝试 stat。
$ stat file1
File: file1
Size: 0 Blocks: 0 IO Block: 4096 regular file
Device: 805h/2053d Inode: 9180838 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ user) Gid: ( 1000/ user)
Access: 2017-06-23 01:37:01.707387495 -0400
Modify: 2017-06-23 01:37:01.707387495 -0400
Change: 2017-06-23 01:37:01.707387495 -0400
Birth: -
如果输出显示纳秒,我们将需要日期来解析(和格式化)时间。
$ stat --printf='%y\n' file1
2017-06-23 01:37:01.707387495 -0400
$ date +'%s%N' -d "$(stat --printf='%y\n' file1)"
1498196221707387495
其余的都是一样的,将file1和file2的结果赋值给两个变量,并进行数值比较。
答案3
如果您愿意使用非嵌入式 Linux,那么您可以使用test
外部命令,它是 GNU coreutils 的一部分。 (test
是大多数 shell 的另一个名称[
,并且是内置的)。它具有纳秒粒度(达到文件系统报告的精度)。
/usr/bin/test "$target" -nt "$source"
该-nt
运算符不是由 POSIX 定义的,但它存在于许多实现中,包括 dash、bash、pdksh、mksh、ATT ksh、zsh、GNU coreutilstest
和 BusyBox。然而,许多实现(dash、bash、pdksh、mksh、BusyBox - 在 Debian jessie 上测试)仅支持 1 秒粒度。
但使用专用于这项工作的工具会是一个更好的主意,例如制作。仅当某个文件比其他文件新时才运行命令是 make 的全部意义。在名为的文件中包含以下内容Makefile
(请注意,每个命令行之前都需要一个制表符)。
target: source
echo This command is only executed if target is newer than source
do_stuff <source >$@
运行make target
以执行生成它的命令。如果target
存在并且比 更新source
,则不会执行命令。阅读 make 的一些文档以获取更多信息。
答案4
POSIXly,你会使用find
:
if find "$source_file" -prune -newer "$target_file" | grep -q '^'; then
printf '%s\n' "$source_file is newer than $target_file"
else
echo "It's not newer or one of the files is not accessible"
fi
对于符号链接,它比较符号链接本身的运行时间。要比较符号链接的目标,请添加-H
或-L
选项。
这假设$source_file
不以其中一个谓词开头-
,也不对应于其中一个find
谓词。如果您需要处理任意文件名,您需要首先执行以下操作:
case $source_file in
(["-+()!"]*) source_file=./$source_file;;
esac
GNU 和 FreeBSDfind
实现至少支持亚秒级粒度。 AFAICT,macos 似乎至少没有在 HFS+ 文件系统的文件属性中存储亚秒级信息。