在 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 | 是一个外壳 @9​​87654329@(参见 man bash 部分的 Pipelinessort -n 表示按第一列排序并将标记视为数字而不是字典(参见man sortcut -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 上的目录中获取最新文件的主要内容,如果未能解决你的问题,请参考以下文章

Linux+Shell获取及拷贝最新备份数据

根据文件名上的 grep 获取到最新文件的自动更新符号链接

Linux+Shell获取及拷贝最新数据到远程设备

VBS获取指定目录下最新文件拷贝然后以当前日期命名

PHP - 如何从目录和子目录中获取最新文件

我想获取gcs每个目录下的最新文件名