我需要编写一个名为的脚本newer.sh
,给定文件列表,返回最新的文件。也就是说,输入:
newer.sh /some/file other-file /and/so/on
将返回列表中最新文件的名称。
到目前为止,这是我能想到的:
#!/bin/bash
echo "Newest file:"
ls -t | head -n 1
它实际上返回根据创建时间创建的最新文件,但仅限于当前目录中的文件。
答案1
使用 GNU 工具:
#! /bin/bash -
export LC_ALL=C
eval "files=($(ls -dt --quoting-style=shell-always -- "${@?}"))"
((${#files[@]} > 0)) && printf '%s\n' "${files[0]}"
对于符号链接,这将考虑符号链接本身的最后修改时间。如果您希望它是符号链接的目标,请将该-L
选项添加到ls
.
如果您想添加限制,即作为参数给出的文件路径不能包含换行符,您可以编写:
#! /bin/sh -
# only works for file paths that don't contain newline characters
[ "$#" -gt 0 ] && ls -td -- "$@" | head -n 1
并消除对 GNU 工具的依赖。
原理是相同的:使用 的ls -t
功能按修改时间对文件进行排序并获取该列表中的第一个。但在这里,ls
每行输出一个文件列表,这使得无法可靠地解析,除非您可以保证没有一个文件路径包含换行符(否则,您将无法区分名为a<newline>b
和两个a
文件b
,如果a<newline>b
是最新的文件,head -n 1
则只会显示a
实际上给出的错误结果)。
与您的方法相比,这里,我们将脚本接收到的参数列表 ( "$@"
) 传递给ls
(在 a 之后--
,以便这些参数被视为ls
文件操作数而不是选项)并传递-d
选项,以便ls
不列出内容这些文件的类型目录。如果脚本收到 0 个参数 ( $#
== 0),我们也不会执行此操作,否则ls -td --
会列出当前目录,因此您将得到.
输出。
除了 GNU 之外ls
,您还可以使用 GNUstat
以可以使用 GNU 排序的方式检索文件的修改时间sort
:
#! /bin/sh -
export TZ=UTC0 LC_ALL=C
stat --printf '%y@%n\0' -- "$@" | sort -rz | sed -z 's/^[^@]*@//;q' | tr '\0' '\n'
(使用最新版本的 GNU sed
for -z
)。或者使用 GNU awk
:
#! /bin/sh -
export TZ=UTC0 LC_ALL=C
stat --printf '%y@%n\0' -- "$@" | awk -v RS='\0' '
NR == 1 || $0 > newest {newest = $0}
END {if (NR) {sub(/[^@]*@/, "", newest); print newest}}'