比较两个文件修改日期

Ale*_*lls 14 stat timestamps files

我正在创建一个通用的编译/转译系统。了解文件是否已经编译/转译的一种方法是比较源文件和目标文件的修改日期。

我需要编写一个可以做到这一点的 bash 脚本:

source_file=foo;
target_file=bar;

stat_source=$(stat source_file);
stat_target=$(stat target_file);
Run Code Online (Sandbox Code Playgroud)

但是如何从统计输出中提取日期并进行比较?有没有比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
Run Code Online (Sandbox Code Playgroud)

AFAICT,时间粒度不超过秒。如果可能的话,我需要得到比这更细粒度的东西。

Tho*_*key 24

鉴于您使用的是stat(类似的功能,但在 BSD 和 GNU 上的输出格式不同),您还可以使用该test实用程序,它直接进行此比较:

   FILE1 -nt FILE2
          FILE1 is newer (modification date) than FILE2

   FILE1 -ot FILE2
          FILE1 is older than FILE2
Run Code Online (Sandbox Code Playgroud)

在你的例子中,

if [ "$source_file" -nt "$target_file" ]
then
    printf '%s\n' "$source_file is newer than $target_file"
fi
Run Code Online (Sandbox Code Playgroud)

该功能在 POSIX 中不可用(请参阅 其文档test),它提供了一个基本原理:

一些新发明的或来自 KornShell 的其他主要作为条件命令 ([[]]) 的一部分出现在早期提案中:s1 >s2, s1 <s2, str = pattern, str != pattern, f1 -ntf2, f1 -otf2, and f1 -ef f2。当从 shell 中删除条件命令时,它们没有被带入 test 实用程序中,因为它们没有包含在 sh 实用程序的历史实现中内置的 test 实用程序中。

尽管该功能得到广泛支持,但未来可能会发生变化

请注意,当操作数是符号链接时,考虑的是符号链接目标的修改时间(这通常是您想要的,find -newer如果不是,请改用)。当符号链接无法解析时,实现之间的行为(有些人认为现有文件总是比无法解析的文件新,如果任何操作数无法解析,有些人将始终报告错误)。

另请注意,并非所有实现都支持亚秒级粒度(从 4.4 版开始,bash's test/[内置仍然不支持,例如,GNUtesttest内置的zshor ksh93,至少在 GNU/Linux 上)。

以供参考:

  • 对于 GNUtest实用程序实现(但请注意,您的 shell,如果fish或类似 Bourne,也会有一个test/[内置函数,通常会隐藏它,使用env test而不是test绕过它),test.c 中的get_mtime读取struct timespec,和
  • 选项-nt使用该数据


小智 4

在这个linux系统上进行测试。测试文件时间的常用方法是 shell:

[ file1 -nt file2 ] && echo "yes"
Run Code Online (Sandbox Code Playgroud)

似乎可以用秒来完成。这将以小于一秒的时间差接触文件,不会检测到该差异:

$ touch file2; sleep 0.1; touch file1; [ file1 -nt file2 ] && echo "yes"
Run Code Online (Sandbox Code Playgroud)

要确认问题(点后的时间为纳秒):

$ 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

将时间提取到两个变量(不带点):

$ file1time=$(ls --time-style=+%s%N -l file1 | awk "{print(\$6)}")
$ file2time=$(ls --time-style=+%s%N -l file2 | awk "{print(\$6)}")
Run Code Online (Sandbox Code Playgroud)

并比较时间(纳秒的时间勉强适合 64 位值。如果您的系统不使用 64 位,则此比较将失败):

$ [ $file1time -gt $file2time ] && echo "yes"
yes
Run Code Online (Sandbox Code Playgroud)

这表明它file1file2


如果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: -
Run Code Online (Sandbox Code Playgroud)

如果输出显示纳秒,我们将需要日期来解析(和格式化)时间。

$ stat --printf='%y\n' file1
2017-06-23 01:37:01.707387495 -0400

$ date +'%s%N' -d "$(stat --printf='%y\n' file1)" 
1498196221707387495
Run Code Online (Sandbox Code Playgroud)

其余的都是一样的,将file1和file2的结果赋值给两个变量,并进行数值比较。