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

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个回答

3
我在我的.profile文件中有一个常用的别名,如下所示:
$ alias | grep xlogs
xlogs='sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | sudo xargs ls -ltr --color | less -R'

所以它可以实现你想要的功能(除了它不能遍历多层更改日期/时间)- 查找最新的文件(在此情况下为*.log和*.trc文件);还只查找最近一天内修改的文件,然后按时间排序并通过less管道输出:
sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | sudo xargs ls -ltr --color | less -R

注意:有些服务器我没有root权限,但总是有sudo权限,所以您可能不需要那部分内容。


这怎么能是“正是你要找的”呢?楼主已经清晰地解释了他想要什么,但是这完全忽略了他的需求。 - hmijail
谢谢指出。你是正确的 - 这种方法不会多级获取更改日期/时间,它只显示其中目录文件的日期/时间。我已编辑了我的答案。 - Tagar

3
我将展示最新访问时间的方法,您可以轻松修改此方法以获取最新修改时间。

有两种方法可以实现此目标:


  1. If you want to avoid global sorting which can be expensive if you have tens of millions of files, then you can do (position yourself in the root of the directory where you want your search to start):

     Linux> touch -d @0 /tmp/a;
     Linux> find . -type f -exec tcsh -f -c test `stat --printf="%X" {}` -gt  `stat --printf="%X" /tmp/a`  ; -exec tcsh -f -c touch -a -r {} /tmp/a ; -print
    

    The above method prints filenames with progressively newer access time and the last file it prints is the file with the latest access time. You can obviously get the latest access time using a "tail -1".

  2. You can have find recursively print the name and access time of all files in your subdirectory and then sort based on access time and the tail the biggest entry:

     Linux> \find . -type f -exec stat --printf="%X  %n\n" {} \; | \sort -n | tail -1
    

那么,这就是它的全部内容...


2
@anubhava的回答 很好,但不幸的是在BSD工具上不能使用 - 也就是说,在macOS上默认安装的 find 将无法使用,因为BSD的find没有-printf运算符。

因此,这里提供一种适用于 macOS + BSD(已在我的 Catalina Mac 上测试)的变体方法,它将 BSD 的 findxargsstat 结合起来:

$ find . -type f -print0 \
      | xargs -0 -n1 -I{} stat -f '%Fm %N' "{}" \
      | sort -rn 

我在这里时,我想分享一下我喜欢使用的BSD命令序列,它能够将时间戳以ISO-8601格式显示。

$ find . -type f -print0 \
    | xargs -0 -n1 -I{} \
       stat  -f '%Sm %N' -t '%Y-%m-%d %H:%M:%S' "{}" \
    | sort -rn

请注意,我的两个答案与@anubhava的不同之处在于,它们将从find传递的文件名作为单个参数传递给xargs,而不是以\0结尾的列表,这会改变最终输出的内容。以下是 GNU 版本(即 @anubhava 的答案,但使用 iso-8601 格式):
$ gfind . -type f -printf "%T+ %p\0" | sort -zk1nr

相关问题:find命令缺少选项-printf,怎么办?


我需要获取最近修改的文件名,所以我添加了 | head -1 | cut -d' ' -f2 以仅获取最新条目的文件名,但你的第一个命令序列让我走上了正确的道路。 - GameSalutes

2

Bash有一行脚本解决方案,用于在多个目录中递归查找最新修改的文件。请使用以下命令和您的目标目录。

 ls -ltr $(find /path/dir1 /path/dir2 -type f)

今天的话题是,在下面的命令中查找今天的日期或时间

 (ls -ltr $(find /path/dir1 /path/dir2 -type f)) |grep -i 'Oct 24'

第一条命令似乎在名称中带有空格的目录上出现了问题。这个命令是否有快速简便的修复方法,还是回到已经发布的上面的命令之一? - Alan

2

这个命令应该可以实现OP指定的功能:

Bash中的一行代码:

$ for first_level in `find . -maxdepth 1 -type d`; do find $first_level -printf "%TY-%Tm-%Td %TH:%TM:%TS $first_level\n" | sort -n | tail -n1 ; done

这将输出类似于:

2020-09-12 10:50:43.9881728000 .
2020-08-23 14:47:55.3828912000 ./.cache
2018-10-18 10:48:57.5483235000 ./.config
2019-09-20 16:46:38.0803415000 ./.emacs.d
2020-08-23 14:48:19.6171696000 ./.local
2020-08-23 14:24:17.9773605000 ./.nano

此列表列出了每个一级目录及其内部最新文件的人类可读时间戳,即使它在子文件夹中,正如在下面引用的

"我需要制作一个列表,其中列出每个一级目录以及其内部最新创建/修改文件的日期和时间。"


1
快速的Bash函数:
# findLatestModifiedFiles(directory, [max=10, [format="%Td %Tb %TY, %TT"]])
function findLatestModifiedFiles() {
    local d="${1:-.}"
    local m="${2:-10}"
    local f="${3:-%Td %Tb %TY, %TT}"

    find "$d" -type f -printf "%T@ :$f %p\n" | sort -nr | cut -d: -f2- | head -n"$m"
}

在一个目录中找到最新修改的文件:
findLatestModifiedFiles "/home/jason/" 1

你可以在第三个参数中指定自己的日期/时间格式。

1
以下内容返回一个包含时间戳和最新文件名的字符串:
find $Directory -type f -printf "%TY-%Tm-%Td-%TH-%TM-%TS %p\n" | sed -r 's/([[:digit:]]{2})\.([[:digit:]]{2,})/\1-\2/' |     sort --field-separator='-' -nrk1 -nrk2 -nrk3 -nrk4 -nrk5 -nrk6 -nrk7 | head -n 1

导致输出形式为: <yy-mm-dd-hh-mm-ss.nanosec> <文件名>

1
对于那些遇到过的人,
stat: unrecognized option: format

当执行来自 Heppo's answer的行 (find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head) 时

请尝试使用-c键替换--format,最后调用将是:

find $1 -type f -exec stat -c '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head

在一些 Docker 容器内,stat 无法使用 --format 选项,这对我有用。


1
“stat” 命令没有得到很好的标准化,因此在不同平台上它接受不同的选项。 “--format”(也称为“-c”)是 Linux 使用的选项(或任何使用 GNU Coreutils 的系统);例如,在 MacOS 上,您需要使用“-f”,并且支持的格式标志也不同。 我猜 Alpine(更新:已确认)或 Busybox 可能使用“-c”而不是“--format”。 - tripleee
1
在Linux(或通常的GNU用户空间)系统中,find -printf可以完成大部分stat所能做的事情,而无需调用外部工具。 - tripleee

0

这也可以用Bash中的递归函数来完成。

设F为一个函数,显示文件时间,必须按字典序排序yyyy-mm-dd等(依赖于操作系统?)

F(){ stat --format %y "$1";}                # Linux
F(){ ls -E "$1"|awk '{print$6" "$7}';}      # SunOS: maybe this could be done easier

R,递归函数,用于遍历目录:

R(){ local f;for f in "$1"/*;do [ -d "$f" ]&&R $f||F "$f";done;}

最后

for f in *;do [ -d "$f" ]&&echo `R "$f"|sort|tail -1`" $f";done

0

对于普通的ls输出,请使用此命令。由于没有参数列表,因此它不会变得太长:

find . | while read FILE;do ls -d -l "$FILE";done

并使用cut使日期、时间和名称更加美观:

find . | while read FILE;do ls -d -l "$FILE";done | cut --complement -d ' ' -f 1-5

编辑:刚刚注意到当前最佳答案按修改日期排序。对于这里的第二个示例,也同样容易,因为每行的修改日期都在最前面 - 在末尾加上一个排序即可:

find . | while read FILE;do ls -d -l "$FILE";done | cut --complement -d ' ' -f 1-5 | sort

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