如何递归查找目录中最新修改的文​​件?

Posted

技术标签:

【中文标题】如何递归查找目录中最新修改的文​​件?【英文标题】:How to recursively find the latest modified file in a directory? 【发布时间】:2011-06-01 12:07:52 【问题描述】:

似乎ls 在进行递归调用时没有正确排序文件:

ls -altR . | head -n 3

如何在目录(包括子目录)中找到最近修改的文件?

【问题讨论】:

How to recursively find and list the latest modified files in a directory with subdirectories and times?的可能重复 【参考方案1】:
find . -type f -printf '%T@ %p\n' \
| sort -n | tail -1 | cut -f2- -d" "

对于一棵大树,sort 可能很难将所有内容都保存在内存中。

%T@ 像 unix 时间戳一样为您提供修改时间,sort -n 按数字排序,tail -1 取最后一行(最高时间戳),cut -f2 -d" " 从输出中删除第一个字段(时间戳)。

编辑:正如-printf 可能仅适用于GNU,stat -c 的ajreals 用法也是如此。虽然在 BSD 上也可以这样做,但格式化选项不同(-f "%m %N" 似乎)

我错过了复数部分;如果你想要更多 最新文件,只需增加 tail 参数即可。

【讨论】:

如果订单很重要,您可以切换使用sort -rn | head -3 而不是sort -n | tail -3。一个版本将文件从最旧到最新,而另一个版本从最新到最旧。 我有一个巨大的目录(大约一万个小文件),我担心性能,但是......这个命令运行不到一秒!太好了,非常感谢!!! :-) "对于一棵巨大的树,排序可能很难将所有内容都保存在内存中。" sort 将根据需要创建临时文件(在 /tmp 中),所以我认为这不是问题。 我发现以下内容更短,输出更易解释:find . -type f -printf '%TF %TT %p\n' | sort | tail -1 如果您知道文件最后一次更改是在过去一周内,可以将选项 -mtime -7 添加到 find 以大大加快处理速度。【参考方案2】:

这给出了一个排序列表:

find . -type f -ls 2>/dev/null | sort -M -k8,10 | head -n5

通过在排序命令中放置“-r”来反转顺序。如果您只想要文件名,请插入“awk 'print $11' |” '| 之前头”

【讨论】:

【参考方案3】:

您可以使用 awk 仅打印修改时间最长的结果(以 unix 时间为单位),而不是对结果进行排序并仅保留最后修改的结果:

find . -type f -printf "%T@\0%p\0" | awk '
    
        if ($0>max) 
            max=$0; 
            getline mostrecent
         else 
            getline
     
    ENDprint mostrecent' RS='\0'

如果文件数量足够大,这应该是解决问题的更快方法。

我使用了 NUL 字符(即 '\0'),因为理论上,文件名可以包含除此之外的任何字符(包括空格和换行符)。

如果您的系统中没有此类病态文件名,您也可以使用换行符:

find . -type f -printf "%T@\n%p\n" | awk '
    
        if ($0>max) 
            max=$0; 
            getline mostrecent
         else 
            getline
     
    ENDprint mostrecent' RS='\n'

此外,这也适用于 mawk。

【讨论】:

这可以很容易地调整以保持三个最新的。 这不适用于 Debian 标准替代方案 mawk 不,但在这种情况下,如果它不打扰你,你可以使用换行符;) 我不知道这是不是因为我在 OSX 上,但这有 一堆 问题。 1. $0 是整行,而不是第一个字段(应该是 $1)。 2. 你不应该使用 getline 因为那会跳过行。 3. 您需要在第一个命令中使用 -0 标志来使用 '\0' 分隔符。 @HarrisonMc 我认为它应该跳过行 - 每个结果输出两行,awk 比较第一行并保存第二行。可以将不那么病态的情况简化为仅输出一行并让 awk 吐出“最大”的那一行。 find . -type f -printf "%TF %TT\t%p\n" | awk 'if ($0>max) max = $0 ENDprint max'【参考方案4】:

跟进@plundra's answer,这是 BSD 和 OS X 版本:

find . -type f -print0 \
| xargs -0 stat -f "%m %N" \
| sort -rn | head -1 | cut -f2- -d" "

【讨论】:

BSD / OS X find 是否支持+ 而不是\;?因为它做同样的事情(将多个文件作为参数传递),没有-print0 | xargs -0 管道。 如果我想按降序获取最后 5 个或 n 个修改的文件怎么办? @khunshan 将head -1 更改为head -5 仅仅因为你开始关心带有-print0 的文件名中的换行符,当你逐行排序时,它不会阻止它。【参考方案5】:

如果在每个文件上单独运行stat 会减慢速度,您可以使用xargs 来加快速度:

find . -type f -print0 | xargs -0 stat -f "%m %N" | sort -n | tail -1 | cut -f2- -d" " 

【讨论】:

【参考方案6】:

这会递归地将当前目录下所有目录的修改时间更改为每个目录下的最新文件:

for dir in */; do find $dir -type f -printf '%T@ "%p"\n' | sort -n | tail -1 | cut -f2- -d" " | xargs -I  touch -r  $dir; done

【讨论】:

如果任何目录包含空格,它会严重损坏 - 需要设置 IFS 并使用引号: IFS=$'\n';for dir in $(find ./ -type d);做回声“$dir”; find "$dir" -type f -printf '%T@ "%p"\n' |排序-n |尾-1 |剪切-f2- -d" " | xargs -I touch -r "$dir";完成;【参考方案7】:

我在 Solaris 10 下找不到最后修改的文件。find 没有printf 选项,stat 不可用。我发现了以下对我很有效的解决方案:

find . -type f | sed 's/.*/"&"/' | xargs ls -E | awk ' print $6," ",$7 ' | sort | tail -1

要显示文件名也可以使用

find . -type f | sed 's/.*/"&"/' | xargs ls -E | awk ' print $6," ",$7," ",$9 ' | sort | tail -1

说明

find . -type f 查找并列出所有文件 sed 's/.*/"&"/' 将路径名括在引号中以处理空格 xargs ls -E 将引用的路径发送到 ls-E 选项确保返回完整的时间戳(格式年-月-日时-分-秒-纳秒awk ' print $6," ",$7 ' 仅提取日期和时间 awk ' print $6," ",$7," ",$9 ' 提取日期、时间和文件名 sort 返回按日期排序的文件 tail -1 只返回最后修改的文件

【讨论】:

【参考方案8】:

在 Ubuntu 13 上,以下操作可能会快一点,因为它颠倒了排序并使用“头”而不是“尾”,从而减少了工作量。在树中显示 11 个最新文件:

找到 . -type f -printf '%T@ %p\n' |排序-n -r |头-11 |剪切-f2- -d" " | sed -e 's,^./,,' | xargs ls -U -l

这提供了一个完整的 ls 列表而无需重新排序,并省略了 'find' 放在每个文件名上的烦人的 './'。

或者,作为 bash 函数:

treecent () 
  local numl
  if [[ 0 -eq $# ]] ; then
    numl=11   # Or whatever default you want.
  else
    numl=$1
  fi
  find . -type f -printf '%T@ %p\n' | sort -n -r | head -$numl |  cut -f2- -d" " | sed -e 's,^\./,,' | xargs ls -U -l

不过,大部分工作还是由 plundra 的原始解决方案完成的。谢谢普伦德拉。

【讨论】:

【参考方案9】:

这似乎工作正常,即使是子目录:

find . -type f | xargs ls -ltr | tail -n 1

如果文件太多,请细化查找。

【讨论】:

-l 选项似乎不需要ls。只需-tr 似乎就足够了。 这似乎是按目录排序的,所以不一定会显示最新的文件 如果文件路径中有空格,最好这样做:find . -type f -print0 | xargs -0 ls -ltr | tail -n 1 这没有为我找到最新的文件。 认为如果文件名中有空格会中断【参考方案10】:

我发现上面的命令很有用,但就我而言,我还需要查看文件的日期和时间,我遇到了几个名称中包含空格的文件的问题。 这是我的工作解决方案。

find . -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" " | sed 's/.*/"&"/' | xargs ls -l

【讨论】:

【参考方案11】:

我更喜欢这个,它更短:

find . -type f -print0|xargs -0 ls -drt|tail -n 1

【讨论】:

【参考方案12】:

这个简单的 cli 也可以工作:

ls -1t | head -1

您可以将 -1 更改为要列出的文件数

【讨论】:

不,不会,因为它不是递归的。【参考方案13】:

显示带有人类可读时间戳的最新文件:

find . -type f -printf '%TY-%Tm-%Td %TH:%TM: %Tz %p\n'| sort -n | tail -n1

结果如下所示:

2015-10-06 11:30: +0200 ./foo/bar.txt

要显示更多文件,请将-n1 替换为更大的数字

【讨论】:

【参考方案14】:

我遇到了同样的问题。我需要递归查找最新的文件。 find 大约需要 50 分钟才能找到。

这里有一个小脚本可以加快速度:

#!/bin/sh

CURRENT_DIR='.'

zob () 
    FILE=$(ls -Art1 $CURRENT_DIR | tail -n 1)
    if [ ! -f $FILE ]; then
        CURRENT_DIR="$CURRENT_DIR/$FILE"
        zob
    fi
    echo $FILE
    exit

zob

这是一个递归函数,用于获取目录的最新修改项。如果此项为目录,则递归调用该函数并搜索该目录等。

【讨论】:

这真的行不通。您可能有一个包含最新文件的旧目录【参考方案15】:

以下命令在 Solaris 上有效:

find . -name "*zip" -type f | xargs ls -ltr | tail -1 

【讨论】:

【参考方案16】:

我一直使用类似的东西,以及最近修改文件的前 k 列表。对于大型目录树,可以更快地避免排序。如果只有前 1 个最近修改的文件:

find . -type f -printf '%T@ %p\n' | perl -ne '@a=split(/\s+/, $_, 2); ($t,$f)=@a if $a[0]>$t; print $f if eof()'

在一个包含 170 万个文件的目录上,我在 3.4 秒内得到最新的文件,与使用排序的 25.5 秒解决方案相比,速度提高了 7.5 倍。

【讨论】:

非常酷:我刚刚用 system("ls -l $f") if eof() 交换了最后一个打印,以便以一种不错的方式查看日期。 @MartinT。 : 太好了,不客气。我很奇怪,当 O(n) 方法可用时,人们有这种对事物进行排序的本能( O(n log n) )。这似乎是唯一避免排序的答案。顺便说一句,我建议的命令的目标只是找到最新文件的路径。你可以在你的shell中给命令起别名(例如lastfile),然后你可以对结果做任何你喜欢的事情,比如ls -l $(lastfile .)open $(lastfile .)(在Mac上)等等。 哦,我的立场是正确的:我在下面看到另一个答案 (@marco)。 +1。【参考方案17】:

我为这个问题写了一个 pypi/github 包,因为我也需要一个解决方案。

https://github.com/bucknerns/logtail

安装:

pip install logtail

用法:更改文件的尾部

logtail <log dir> [<glob match: default=*.log>]

用法2:在编辑器中打开最新更改的文件

editlatest <log dir> [<glob match: default=*.log>]

【讨论】:

【参考方案18】:

使用 find — 带有漂亮且快速的时间戳

以下是如何在包含子目录的目录中查找和列出最新修改的文​​件。 故意忽略隐藏文件。可以自定义时间格式。

$ find . -type f -not -path '*/\.*' -printf '%TY-%Tm-%Td %TH:%TM %Ta %p\n' |sort -nr |head -n 10

结果

很好地处理文件名中的空格 - 不是应该使用这些!

2017-01-25 18:23 Wed ./indenting/Shifting blocks visually.mht
2016-12-11 12:33 Sun ./tabs/Converting tabs to spaces.mht
2016-12-02 01:46 Fri ./advocacy/2016.Vim or Emacs - Which text editor do you prefer?.mht
2016-11-09 17:05 Wed ./Word count - Vim Tips Wiki.mht

更多

Morefind 关注链接。

【讨论】:

【参考方案19】:

我发现以下内容更短,输出更易解释:

find . -type f -printf '%TF %TT %p\n' | sort | tail -1

鉴于标准化 ISO 格式日期时间的固定长度,字典排序很好,我们不需要在排序时使用 -n 选项。

如果要再次删除时间戳,可以使用:

find . -type f -printf '%TFT%TT %p\n' | sort | tail -1 | cut -f2- -d' '

【讨论】:

【参考方案20】:

在 /target_directory 及其所有子目录中搜索在过去 60 分钟内被修改的文件:

$ find /target_directory -type f -mmin -60

要查找最近修改的文件,按更新时间的倒序排序(即最近更新的文件在前):

$ find /etc -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort -r

【讨论】:

【参考方案21】:

在使用基于 find 的解决方案多年后,我发现自己希望能够排除像 .git 这样的目录。

我切换到这个基于rsync 的解决方案。把这个放在~/bin/findlatest:

#!/bin/sh
# Finds most recently modified files.
rsync -rL --list-only "$@" | grep -v '^d' | sort -k3,4r | head -5

现在findlatest . 将列出最近修改的 5 个文件,findlatest --exclude .git . 将列出 .git 中排除的 5 个文件。

这通过利用一些很少使用的 rsync 功能来工作:“如果在没有目标的情况下指定了单个源 arg [to rsync],则文件以类似于 ls -l 的输出格式列出”(rsync 手册页)。

使用 rsync 参数的能力与基于 rsync 的备份工具结合使用非常有用。比如我用rsnapshot,我用rsnapshot.conf这一行备份了一个应用目录:

backup  /var/atlassian/application-data/jira/current/   home    +rsync_long_args=--archive --filter="merge /opt/atlassian/jira/current/backups/rsync-excludes"

rsync-excludes 列出了我不想备份的目录:

- log/
- logs/
- analytics-logs/
- tmp/
- monitor/*.rrd4j

我现在可以看到将要备份的最新文件:

findlatest /var/atlassian/application-data/jira/current/ --filter="merge /opt/atlassian/jira/current/backups/rsync-excludes"

【讨论】:

以上是关于如何递归查找目录中最新修改的文​​件?的主要内容,如果未能解决你的问题,请参考以下文章

从 Azure Blob 获取最新修改的文​​件

Ant:如何从目录中选择最新的修改文件?

在 PowerShell 中查找最新修改的文​​件信息

Linux 递归修改后缀名

如何在目录中找到最新修改文件的时间戳(递归)?

在bash中对所有最近修改过的文件运行脚本