如何递归查找并列出包含子目录的目录中最新修改的文件和时间?

558
  • 操作系统:Linux

  • 文件系统类型:ext3

  • 首选解决方案:Bash(脚本/一行命令)、Ruby或Python

我有多个目录,每个目录下都有多个子目录和文件。我需要生成一个列表,列出所有这些目录,并按照构建方式列出每个一级目录的日期和时间,以及该目录中最新创建/修改的文件的日期和时间。

为了澄清,如果我在几个子目录层次下触摸文件或修改其内容,则该时间戳应显示在一级目录名称旁边。假设我有一个结构如下所示的目录:

./alfa/beta/gamma/example.txt

当我修改文件example.txt的内容时,我需要在一级目录alfa旁边以人类可读的形式显示时间,而不是epoch。我尝试过使用find、xargssort等方法,但无法解决的问题是当我在几个层次下创建/修改文件时,'alfa'的文件系统时间戳并不会改变。


1
如果您能够承受建立它的痛苦,https://github.com/shadkam/recentmost 可以被使用。 - user3392225
我只看到了大量的东西,而不是像 -R 开关这样的解决方案。 - neverMind9
1
@user3392225 GitHub / shadkam / recentmost 的分支可以在 https://github.com/ConradHughes/recentmost 找到,使用 -0 选项与 find-print0 配合使用。 - Setaa
22个回答

626

试试这个:

#!/bin/bash
find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head

将路径作为参数传递给该命令,它将从指定目录开始递归扫描(支持包含空格的文件名)。

如果有大量文件,则可能需要等待一段时间才能返回结果。如果使用xargs,可以提高性能:

#!/bin/bash
find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head

这会快一点。


153
你的“快速方法”还应该能够使用print0来支持文件名中的空格和换行符。这是我使用的命令:find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head。尽管如此,对于我来说它仍然非常快。 - Dan
3
我发现我查看的一些目录无法使用stat命令,所以我对“快速”命令进行了以下更改,以避免在最终输出中看到错误信息。 find ${1} -type f | xargs stat --format '%Y :%y %n' 2>/dev/null | sort -nr | cut -d: -f2- - TJ L
22
在Mac OS X上,由于不是GNU的stat,因此该命令无法正常运行。您需要执行brew install coreutils并使用gstat代替stat - CharlesB
41
你不需要运行 stat 命令,因为 find PATH -type f -printf "%T@ %p\n"| sort -nr 命令可以完成相同的工作。这样做还会稍微快一点。 - n.r.
12
在Mac OS X上,无需安装gstat或其他任何内容,您可以执行以下操作:find PATH -type f -exec stat -f“%m%N” "{}" \; | sort -nr | head - cobbzilla
显示剩余9条评论

283

查找所有文件状态最后更改时间为N分钟前的文件:

find -cmin -N
例如:
find -cmin -5

使用-ctime代替-cmin来表示天数:

find -ctime -3

在FreeBSD和MacOS上,您还可以使用-ctime n[smhdw]表示秒、分钟、小时、日和星期。如果未提供单位,则默认为天。

示例:

# FreeBSD and MacOS only:
find . -ctime -30s
find . -ctime -15
find . -ctime -52w

1
它比其他解决方案更快,且更简单。 - david.perez
34
非常好,你还可以使用“find -ctime -50”的命令来查找最近50天的更改。 - Gorkem
1
为了排除杂乱,使用 sudo find -cmin -1 2>&1 |grep -v /proc/ - Cees Timmerman
很好的想法。首先考虑对目录执行 stat . 命令,以获取你应该查看的修改日期的概念。 - yǝsʞǝla
2
通常我想使用 find -type f 而不是 find 来仅显示文件而非目录。 - krlmlr
显示剩余4条评论

56

GNU find(请参见man find)具有 -printf 参数,用于显示文件的Epoch mtime和相对路径名。

redhat> find . -type f -printf '%T@ %P\n' | sort -n | awk '{print $2}'

3
谢谢!这是唯一一个可以在合理的时间内搜索我的非常广泛目录结构的答案。我使用 tail 命令来防止输出打印出成千上万行。 - sffc
11
另一个评论:当文件名包含空格时,awk '{print $2}' 部分似乎会引起问题。这里提供了使用 sed 的解决方法,并且除了路径之外还打印了时间:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//' - sffc
3
我认为应该使用“sort -rn”进行排序。 - Bojan Dević
2
“-printf” 变量比每次调用 “stat” 进程要快得多,这为我的备份工作节省了数小时的时间。感谢让我意识到这一点。 我避免使用 awk/sed,因为我只关心树中的最后更新时间 - 所以 X=$(find /path -type f -printf '%T %p\n' | grep -v something-I-don-tcare-about | sort -nr | head -n 1) 和 echo ${X#*" "} 对我很有用(给我第一个空格之前的内容)。 - David Goodwin
2
如果文件名跨越多行,所有操作都将无法正常运作。使用 touch "lala<Enter>b" 命令来创建这样的文件。我认为 Unix 工具在文件名方面存在着重大缺陷。 - 林果皞
@林果皞 是的,但是a) 如果你的节点包含换行符,那么你就会遇到麻烦;b) 你可以使用find的“-print0”标志来输出以null字符分隔的项目,并相应地处理它们;或者在这种情况下,使用“-printf...\0”而不是“-printf...\n”。 - laur

39

我把 Daniel Böhmer 的精彩回答 简化成了这个一行代码:

stat --printf="%y %n\n" $(ls -tr $(find * -type f))
如果文件名中有空格,您可以使用以下修改方法:
OFS="$IFS";IFS=$'\n';stat --printf="%y %n\n" $(ls -tr $(find . -type f));IFS="$OFS";

这样怎么样:IFS=$'\n'; stat --printf="%y %n\n" $(ls -tr $(find . -type f)) - slashdottir
3
如果你有非常多的文件,这种方法可能行不通。使用 xargs 的答案可以解决这个限制。 - carl verbiest
@carlverbiest,确实有大量的文件会破坏 slashdottir 的解决方案。即使是基于 xargs 的解决方案也会变得很慢。user2570243 的解决方案是针对大型文件系统最好的选择。 - Stéphane Gourichon
在处理文件名时,“IFS=$'\n'”无论如何都不安全:UNIX中的文件名中可以包含换行符。只有NUL字符保证不会出现在路径中。 - Charles Duffy

18

试试这个:

#!/bin/bash
stat --format %y $(ls -t $(find alfa/ -type f) | head -n 1)

它使用find从目录中收集所有文件,ls按修改日期排序列出它们,head选择第一个文件,最后stat以漂亮的格式显示时间。

目前对于名称中带有空格或其他特殊字符的文件不安全。如果不符合您的需求,请写下评论。


2
你好:我喜欢你的答案,它可以很好地工作并打印出正确的文件。然而,在我的情况下,它并没有帮助我,因为有太多的子级目录。所以当我运行ls命令时会出现“参数列表过长”的错误...而xargs在这种情况下也无法帮助我。我会尝试其他方法。 - fredrik
在这种情况下,它会更加复杂,需要一些真正的编程。我将使用 Perl 进行编码。 - Daniel Böhmer
1
我改用 PHP 解决了这个问题。使用递归函数遍历文件系统树,并存储最近修改文件的时间。 - fredrik
1
在MacOS上,我需要使用stat $(ls -t $(find alfa/ -type f) | head -n 10)--format会变成-f,但是没有%y,所以我没有费心找替代品。 - Michael Bolli

12

忽略隐藏文件-带有友好和快速的时间戳

以下是如何查找和列出目录中包含子目录的最新修改文件。故意忽略隐藏文件。虽然文件名中的空格被很好地处理了,但您不应该使用那些!时间格式可以自定义。

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

2017.01.25 18h23 Wed ./indenting/Shifting blocks visually.mht
2016.12.11 12h33 Sun ./tabs/Converting tabs to spaces.mht
2016.12.02 01h46 Fri ./advocacy/2016.Vim or Emacs - Which text editor do you prefer?.mht
2016.11.09 17h05 Wed ./Word count - Vim Tips Wiki.mht

通过访问此链接,可以找到更多find的相关内容。


11

这个命令可在 Mac OS X 上使用:

find "$1" -type f -print0 | xargs -0 gstat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head

在 Linux 上,就像原帖的问题,使用 stat 替换 gstat

当然,这个答案来自user37078出色的解决方案,并从评论中晋升为完整的答案。我混合了CharlesB的见解,在 Mac OS X 上使用 gstat。顺便说一下,我是从MacPorts获取的coreutils,而不是Homebrew

以下是我将其打包成一个简单命令~/bin/ls-recent.sh以供重用的方法:

#!/bin/bash
# ls-recent: list files in a directory tree, most recently modified first
#
# Usage: ls-recent path [-10 | more]
#
# Where "path" is a path to target directory, "-10" is any argument to pass
# to "head" to limit the number of entries, and "more" is a special argument
# in place of "-10" which calls the pager "more" instead of "head".
if [ "more" = "$2" ]; then
   H=more; N=''
else
   H=head; N=$2
fi

find "$1" -type f -print0 |xargs -0 gstat --format '%Y :%y %n' \
    |sort -nr |cut -d: -f2- |$H $N

2
在OS X Yosemite上,我遇到了错误:find: ftsopen: 没有那个文件或目录。 - Reece
有趣。你输入了什么命令(带参数)?那个目录中的文件名是什么?如果你创建了自己的版本 ~/bin/ls-recent.sh,你是否仔细检查了脚本的差异? - Jim DeLaHunt
13
对于那些不想在Mac OS X上安装任何东西的人:find . -exec stat -f'%m%t%Sm %N' {} + | sort -n | cut -f2-。该命令将按修改时间对当前目录下的文件进行排序,并以易于阅读的格式输出它们的文件名和修改时间。 - Jake
@Jake:我认为你的评论应该被提升为完整的答案。这正是Mac用户正在寻找的。谢谢! - Andreas Rayo Kniep
根据@Jake的建议,我最终使用了以下命令:find . -type f -exec stat -f '%m%t%Sm %N' {} + | sort -nr | cut -f2- | grep -v ".DS_Store" | head -10。更改包括反向排序以将最新的文件显示在顶部,并通过head仅保留最近的10个文件,同时使用-type fgrep过滤掉目录和DS_Store文件。 - Bar

10

这是我正在使用的(非常高效):

function find_last () { find "${1:-.}" -type f -printf '%TY-%Tm-%Td %TH:%TM %P\n' 2>/dev/null | sort | tail -n "${2:-10}"; }

优点:

  • 无论扫描多少文件,它只会生成3个进程
  • 适用于包含空格的文件名
  • 适用于大量文件

使用方法:

find_last [dir [number]]

其中:

  • dir - 要搜索的目录[当前目录]
  • number - 要显示的最新文件数[10]

find_last /etc 4的输出如下:

2019-07-09 12:12 cups/printers.conf
2019-07-09 14:20 salt/minion.d/_schedule.conf
2019-07-09 14:31 network/interfaces
2019-07-09 14:41 environment

5

在这篇文章中,Perl和Python的解决方案都帮助我在Mac OS X上解决了这个问题:

如何递归按修改日期排序列出文件(无stat命令可用!)

引用自该帖子:

Perl:

find . -type f -print |
perl -l -ne '
    $_{$_} = -M;  # store file age (mtime - now)
    END {
        $,="\n";
        print sort {$_{$b} <=> $_{$a}} keys %_;  # print by decreasing age
    }'

Python:

find . -type f -print |
python -c 'import os, sys; times = {}
for f in sys.stdin.readlines(): f = f[0:-1]; times[f] = os.stat(f).st_mtime
for f in sorted(times.iterkeys(), key=lambda f:times[f]): print f'

4

以下是适用于文件名可能包含空格、换行符和 glob 字符的版本:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printf会打印文件的修改时间(Epoch 值),后跟一个空格和以 \0 结尾的文件名。
  • sort -zk1nr读取以 NUL 结尾的数据并按照数字逆序排序。

由于该问题标记了 Linux,我假设 GNU 核心实用程序可用。

您可以将上述内容与以下内容连接:

xargs -0 printf "%s\n"

打印修改时间和按修改时间排序的文件名(最近的先)并以换行符终止。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接