在 Linux 上的目录中获取最新文件
Posted
技术标签:
【中文标题】在 Linux 上的目录中获取最新文件【英文标题】:Get most recent file in a directory on Linux 【发布时间】:2010-11-04 04:19:26 【问题描述】:寻找将返回目录中单个最新文件的命令。
没有看到ls
的限制参数...
【问题讨论】:
watch -n1 'ls -Art | tail -n 1'
- 显示最后的文件
这里的大多数答案解析ls
的输出或使用find
而不使用-print0
,这对于处理烦人的文件名是有问题的。总是有用的:BashFAQ099,它给出了这个问题的 POSIX 答案
也很有用:unix.stackexchange.com/questions/29899/…
更新但类似的问题在这里:Bash script to find and display oldest file
【参考方案1】:
ls -Art | tail -n 1
不是很优雅,但很有效。
使用的标志:
-A
列出除.
和..
之外的所有文件
-r
排序时倒序
-t
按时间排序,最新的在前
【讨论】:
使用 ls -Artls 还可以查看文件日期。 一个小问题:此版本将ls
的所有输出通过管道传输到tail
,然后仅打印最后一行。恕我直言,最好按升序排序并改用head
,正如chaos
建议的那样。打印第一行后,head 退出,因此发送下一行(实际上是下一个块)将上升一个 SIGPIPE,ls
也将退出。
@TrueY 我非常同意。 Chaos 的答案更有效率。
请注意,此解决方案包括目录和文件。这可能是好事也可能是坏事;这取决于个人用户想要什么。我提出这个问题的原因是最初的问题是“文件”。
我认为使用 tail 代替 head 的预期好处可能是排除包含描述由 ls 返回的总数的行。【参考方案2】:
ls -t | head -n1
这个命令实际上给出了当前工作目录中最新修改的文件。
【讨论】:
相当于我的,而且可能更高效。 @Josir 这可以修改为包含带有ls -tl | head -n 1
的日期戳,并且它不必像我的那样将整个表推入管道。
@noɥʇʎԀʎzɐɹƆ ls -t *.png | head -n1
如果它是最新修改的,则返回一个文件夹,如果您特别想要最新修改的文件,请使用:ls -Art | head -n1
@achasinh 将head
替换为tail
。理想情况下还包括--group-directories-first
。【参考方案3】:
这是一个递归版本(即它在某个目录或其任何子目录中找到最近更新的文件)
find /dir/path -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1
命令行的简单外行解释:
find /dir/path -type f
查找目录下的所有文件
-printf "%T@ %p\n"
为每个文件打印一行,其中 %T@
是自 1970 纪元以来的浮点秒数,%p
是文件名路径,\n
是换行符
更多信息见man find
|
是一个外壳 @987654329@(参见 man bash
部分的 Pipelines
)
sort -n
表示按第一列排序并将标记视为数字而不是字典(参见man sort
)
cut -d' ' -f 2-
表示使用
字符分割每一行,然后打印从第二个标记开始的所有标记(参见man cut
)
注意:-f 2
只会打印第二个标记
tail -n 1
表示打印最后一行(见man tail
)
【讨论】:
这是适用于 Mac 的解决方案:find $DIR -type f -exec stat -lt "%Y-%m-%d" \+ | cut -d' ' -f6- | sort -n | tail -1
问题是 cut 命令用空格截断路径而不是 -f 2 使用 -f2- 将字段 2 返回到行尾
如chaos的建议,如果你用sort -nr
倒序排序,你可以用head -n 1
代替tail -n 1
,稍微提高效率。 (尽管如果您正在对递归查找进行排序,获取第一行或最后一行不会是缓慢的部分。)
非常有用!我发现如果通过管道传输到 ls 会更友好:find ./ -type f -printf "%T@ %p\n" -ls | sort -n | cut -d' ' -f 2- | tail -n 1 | xargs -r ls -lah
@user 的 Mac 版本只排序/显示日期,而不是时间。这是一个固定版本:find $DIR -type f -exec stat -lt "%F %T" \+ | cut -d' ' -f6- | sort -n | tail -1
【参考方案4】:
关于可靠性的说明:
由于换行符与文件名中的任何字符一样有效,因此任何依赖 行 的解决方案(例如基于 head
/tail
的解决方案都是有缺陷的。
对于 GNU ls
,另一种选择是使用 --quoting-style=shell-always
选项和 bash
数组:
eval "files=($(ls -t --quoting-style=shell-always))"
(($#files[@] > 0)) && printf '%s\n' "$files[0]"
(如果您还想考虑隐藏文件,请将-A
选项添加到ls
)。
如果您想限制常规文件(忽略目录、fifos、设备、符号链接、套接字...),您需要求助于 GNU find
。
使用 bash 4.4 或更新版本(readarray -d
)和 GNU coreutils 8.25 或更新版本(cut -z
):
readarray -t -d '' files < <(
LC_ALL=C find . -maxdepth 1 -type f ! -name '.*' -printf '%T@/%f\0' |
sort -rzn | cut -zd/ -f2)
(($#files[@] > 0)) && printf '%s\n' "$files[0]"
或递归:
readarray -t -d '' files < <(
LC_ALL=C find . -name . -o -name '.*' -prune -o -type f -printf '%T@%p\0' |
sort -rzn | cut -zd/ -f2-)
最好使用zsh
及其全局限定符而不是bash
来避免所有这些麻烦:
当前目录中最新的常规文件:
printf '%s\n' *(.om[1])
包括隐藏的:
printf '%s\n' *(D.om[1])
第二个最新:
printf '%s\n' *(.om[2])
符号链接解析后检查文件年龄:
printf '%s\n' *(-.om[1])
递归:
printf '%s\n' **/*(.om[1])
此外,启用补全系统(compinit
和 co)后,Ctrl+Xm 将成为扩展至最新文件的补全器。
所以:
vi Ctrl+Xm
会让你编辑最新的文件(你也有机会在按 Return 之前查看它)。
vi Alt+2Ctrl+Xm
对于第二个最新的文件。
vi *.cCtrl+Xm
获取最新的c
文件。
vi *(.)Ctrl+Xm
对于最新的常规文件(不是目录,也不是fifo/设备...),等等。
【讨论】:
感谢zsh
的提示。您能否在 zsh docs 中提供更多详细信息的链接?
@freezed,见info zsh qualifiers
谢谢@Stephane Chazelas
我喜欢*(.om[1])
,但我通常想在一组文件夹中找到最新的文件,即。 /path/to/folders*/*(.om[1])
,不幸的是它只返回所有匹配文件夹中的最新文件。请参阅unix.stackexchange.com/questions/552103/…,了解如何在多个文件夹中完成它。
@SergioAraujo, c
like find
's -ctime
用于 inode 更改时间,与 creation无关> 时间。 mtime
可以看作是文件内容的创建时间(因为它们永远不会一次性创建)。一些系统和文件系统记录 birth*/*creation 时间,这是文件的 inode 产生(可能再次)存在的时间,但是没有可移植的 API 来检索它,并且 zsh 还没有相应的排序限定符。但是那个特定的时间并不是特别有用。见When was file created【参考方案5】:
我用:
ls -ABrt1 --group-directories-first | tail -n1
它只给了我文件名,不包括文件夹。
【讨论】:
除非目录只有目录【参考方案6】:ls -lAtr | tail -1
其他解决方案不包含以'.'
开头的文件。
此命令还将包括'.'
和'..'
,这可能是也可能不是您想要的:
ls -latr | tail -1
【讨论】:
这个会返回“。”如果目录的修改时间比包含的任何文件(比如删除)更晚?也许这是期望的行为,也许不是。但无论哪种方式你都是对的。【参考方案7】:在文件数量变得非常大(如整个文件系统)之前,查找/排序解决方案非常有效。使用 awk 来跟踪最新的文件:
find $DIR -type f -printf "%T@ %p\n" |
awk '
BEGIN recent = 0; file = ""
if ($1 > recent)
recent = $1;
file = $0;
END print file; ' |
sed 's/^[0-9]*\.[0-9]* //'
【讨论】:
即使在大型目录或文件系统上,它也能非常快速准确地运行。非常优雅和高效的解决方案。谢谢!【参考方案8】:我喜欢echo *(om[1])
(zsh
语法),因为它只给出文件名,不调用任何其他命令。
【讨论】:
如果您使用的是 zsh,则可以正常工作。你不使用zsh吗?我认为 bash 是 Ubuntu 中的默认设置;你可以安装 zsh,或者你可以找到一个等效的 bash 命令。 命令为“echo”;它只是评估其参数并将它们发送到标准输出。在这种情况下,glob 限定符“om[1]”具有“o”,这意味着按照“m”对匹配文件进行排序,即修改时间。从该有序列表中,获取第一个文件 [1]。您可以阅读有关 glob 限定符的信息:zsh.sourceforge.net/Doc/Release/Expansion.html#Glob-Qualifiers【参考方案9】:根据 dmckee 的回答缩短变体:
ls -t | head -1
【讨论】:
但是-1
的 head 语法在很久以前就被弃用了,应该使用 -n 1
。【参考方案10】:
如果您想获取最新更改的文件,还包括任何子目录,您可以使用这个小单行:
find . -type f -exec stat -c '%Y %n' \; | sort -nr | awk -v var="1" 'NR==1,NR==var print $0' | while read t f; do d=$(date -d @$t "+%b %d %T %Y"); echo "$d -- $f"; done
如果您不想对已更改的文件执行相同的操作,但对于访问的文件,您只需更改
%Y 参数从 stat 命令到 %X。您最近访问的文件的命令如下所示:
find . -type f -exec stat -c '%X %n' \; | sort -nr | awk -v var="1" 'NR==1,NR==var print $0' | while read t f; do d=$(date -d @$t "+%b %d %T %Y"); echo "$d -- $f"; done
对于这两个命令,如果您想列出多个文件,也可以更改 var="1" 参数。
【讨论】:
【参考方案11】:我个人更喜欢尽可能少地使用非内置的bash
命令(以减少昂贵的 fork 和 exec 系统调用的数量)。要按日期排序,需要调用ls
。但是使用head
并不是真的必要。我使用以下单行(仅适用于支持名称管道的系统):
read newest < <(ls -t *.log)
或获取最旧文件的名称
read oldest < <(ls -rt *.log)
(注意两个“
如果还需要隐藏文件 - 可以添加 arg。
我希望这会有所帮助。
【讨论】:
您应该说明文件名存储在 $oldest/newest var 中。但是对于最少数量的分叉 +1。 @squareatom 我认为 read 是内置的。但你是对的,也许不是。感谢您的评论! 假设您知道内置是什么,您可能是正确的。但是,我只是在考虑OP的问题。他们要求一个可以返回某些东西的命令。从技术上讲,您的解决方案不会输出任何内容。所以只需在末尾添加“&& echo $newest”或类似的;-)【参考方案12】:使用 R 递归选项 .. 您可以在此处将其视为对良好答案的增强
ls -arRtlh | tail -50
【讨论】:
【参考方案13】:只有 Bash 内置函数,紧跟 BashFAQ/003:
shopt -s nullglob
for f in * .*; do
[[ -d $f ]] && continue
[[ $f -nt $latest ]] && latest=$f
done
printf '%s\n' "$latest"
【讨论】:
【参考方案14】:ls -t -1 | sed '1q'
将显示文件夹中最后修改的项目。与grep
配对以查找带有关键字的最新条目
ls -t -1 | grep foo | sed '1q'
【讨论】:
什么是'1q'?1
是第一行相当于 head -n 1
什么是 q?【参考方案15】:
试试这个简单的命令
ls -ltq <path> | head -n 1
如果你想要文件名 - 最后修改,路径 = /ab/cd/*.log
如果您想要目录名称 - 最后修改,路径 = /ab/cd/*/
【讨论】:
【参考方案16】:递归:
find $1 -type f -exec stat --format '%Y :%y %n' "" \; | sort -nr | cut -d: -f2- | head
【讨论】:
【参考方案17】:根据模式在每个目录中查找最新文件,例如名称以“tmp”结尾的工作目录的子目录(不区分大小写):
find . -iname \*tmp -type d -exec sh -c "ls -lArt | tail -n 1" \;
【讨论】:
【参考方案18】:假设您不关心以.
开头的隐藏文件
ls -rt | tail -n 1
否则
ls -Art | tail -n 1
【讨论】:
【参考方案19】:ls -Frt | grep "[^/]$" | tail -n 1
【讨论】:
还有其他答案提供了 OP 的问题,它们是多年前发布的。发布答案时,请确保添加新的解决方案或更好的解释,尤其是在回答较旧的问题时。 @help-info.de 请不要投票删除仅代码的答案。他们可能不是很好,但deletion is for things that are not answers @Machavity - 我希望不要失败,只为迟到的答案发表评论。【参考方案20】:所有这些 ls/tail 解决方案对于 a 目录中的文件都可以正常工作 - 忽略子目录。
为了在搜索中包含所有文件(递归),可以使用 find。 gioele 建议对格式化的查找输出进行排序。但要小心空格(他的建议不适用于空格)。
这应该适用于所有文件名:
find $DIR -type f -printf "%T@ %p\n" | sort -n | sed -r 's/^[0-9.]+\s+//' | tail -n 1 | xargs -I ls -l ""
这是按 mtime 排序的,见 man find:
%Ak File's last access time in the format specified by k, which is either `@' or a directive for the C `strftime' function. The possible values for k are listed below; some of them might not be available on all systems, due to differences in `strftime' between systems.
@ seconds since Jan. 1, 1970, 00:00 GMT, with fractional part.
%Ck File's last status change time in the format specified by k, which is the same as for %A.
%Tk File's last modification time in the format specified by k, which is the same as for %A.
所以只需将%T
替换为%C
即可按ctime 排序。
【讨论】:
您对@gioele 建议的空白问题是正确的,但您只需要在 -f 选项中添加一个连字符以使其成为修复该问题的范围:find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 2- | tail -n 1
或者,保留除第一个字段外的所有字段:find $DIR -type f -printf "%T@ %p\n" | sort -n | cut -d' ' -f 1 --complement | tail -n 1
【参考方案21】:
我也需要这样做,我找到了这些命令。这些对我有用:
如果您希望最后一个文件在文件夹中的创建日期(访问时间):
ls -Aru | tail -n 1
如果您希望最后一个文件的内容发生变化(修改时间):
ls -Art | tail -n 1
【讨论】:
以上是关于在 Linux 上的目录中获取最新文件的主要内容,如果未能解决你的问题,请参考以下文章