在 POSIX sh 中执行 -nt/-ot 测试

Kus*_*nda 11 posix timestamps files test

内置test[实用程序在大多数 shell 中都有-nt("newer than") 和-ot("older than") 测试,即使 shell 以“POSIX 模式”运行(对于同名的外部实用程序也是如此)我可以访问的系统)。这些测试用于比较两个文件的修改时间戳。它们记录的语义在不同的实现中略有不同(关于如果一个或另一个文件不存在会发生什么),但它们未包含在POSIX 规范中。为test实用程序

他们不结转到test时,从[KornShell]壳去除有条件的命令,因为他们没有被列入工具test内置于历史实现实用sh工具。

假设我想比较/bin/shshell 脚本中文件之间的修改时间戳,然后根据一个文件是否比另一个文件新来采取行动,如

if [ "$sigfile"     -nt "$timestamp" ] ||
   [ "$sigfile.tmp" -nt "$timestamp" ]
then
    return
fi
Run Code Online (Sandbox Code Playgroud)

...除了make(至少可以说脚本的其余部分变得笨拙)之外,我还可以使用什么其他实用程序?或者我应该假设没有人会在“历史实现sh”上运行脚本,或者辞职为特定的shell编写,例如bash

cuo*_*glm 13

POSIXLY:

f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
    printf '%s is newer than %s\n' "$f1" "$f2"
fi
Run Code Online (Sandbox Code Playgroud)

使用文件的绝对路径可防止文件名仅包含换行符的误报。

如果使用相对路径,则将find命令更改为:

find -L "$f1" -prune -newer "$f2" -exec echo . \;
Run Code Online (Sandbox Code Playgroud)

  • @Kusalananda AFAICT,`find` 的退出状态与`find` 的各个测试返回的内容无关 - 除非在实际运行这些测试时可能出现错误。即使没有找到文件,`find` 也会退出 0。 (3认同)

meu*_*euh 8

这可能是使用最古老的 Unix 命令之一的情况,ls.

x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]
Run Code Online (Sandbox Code Playgroud)

如果 a 比 b 新,则结果为真。

  • 如果文件名以 `-` 开头(缺少 `--`),这将不起作用。你需要 `-L` 来让它等价于 `-nt`。例如,这不适用于比较 `x` 和 `$'x\nx'`。 (3认同)

sch*_*ily 5

您提出了一个有趣的问题并提出了一个应该首先得到验证的主张。

我检查了以下行为:

$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'
Run Code Online (Sandbox Code Playgroud)

与各种贝壳。结果如下:

  • bash不起作用 - 不打印任何内容。

  • 波什 作品

  • dash不起作用 - 不打印任何内容。

  • ksh88不起作用 - 不打印任何内容。

  • ksh93 工作原理

  • mksh不起作用 - 不打印任何内容。

  • posh打印: posh: [: -nt: 意外的运算符/操作数

  • 亚什 作品

  • zsh 在较新版本中工作,较旧版本不打印任何内容

因此,九个 shell 中有四个支持-nt功能并正确实现它。在这种情况下,正确意味着:能够比较支持亚秒时间戳粒度的最新平台上的时间戳。请注意,我选择的文件的时间戳通常仅存在几微秒的差异。

由于更容易找到有效的find实现,我建议替换

if [ "$file1" -nt "$file2" ] ; then
    echo newer
fi
Run Code Online (Sandbox Code Playgroud)

通过find基于表达式。

if [ "$( find "$file1" -newer "$file2" )" ]; then
    echo newer
fi
Run Code Online (Sandbox Code Playgroud)

至少只要$file1不包含换行符就可以工作。

if [ "$( find -L "$file1" -newer "$file2" -exec echo newer \; )" ]; then
    echo newer
fi
Run Code Online (Sandbox Code Playgroud)

有点慢但工作正常。

顺便说一句:关于 make,我不能代表所有 make 实现,但SunPro Make支持与纳秒粒度的时间比较,因为大约。20 年过去了,最近smakegmake添加了这个功能。

  • 我认为反对票可能是关于对抗性措辞。在这里,将其称为限制(在某些情况下很重要)会更公平。一些“查找”实现(例如 busybox 或 heirloom-toolchest)将具有相同的限制。 (3认同)
  • 您需要“-L”才能使“find”版本等同于“-nt”。对于以 `-` 或 `!`、`(`... 开头的文件名,它也会失败。 (3认同)
  • 事实上,当我使用对抗性措辞时,我确实经常遭到反对。如果您在答案开始时说明上下文(截断为第二个分辨率时在同一时间戳内修改的文件),则会有所帮助。 (2认同)