如何在具有子目录和时间的目录中递归地查找和列出最新的修改文件?


417
  • 作业系统:Linux

  • 文件系统类型:ext3

  • 首选解决方案:bash(脚本/脚本),ruby,python

我有几个目录,其中有几个子目录和文件。我需要列出所有这些目录的清单,这些清单的构建方式应使其中每个第一级目录都在其中最新创建/修改的文件的日期和时间旁边列出。

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

./alfa/beta/gamma/example.txt

并且我修改了文件的内容example.txt,我需要将该时间alfa以人类可读的形式(而不是时期)显示在第一级目录旁边。我已经使用find尝试了一些东西,xargssort和喜欢,但我不能得到解决,当我创建“阿尔法”的文件系统时间戳不会更改/修改文件几级下降的问题。


如果您不愿意构建它,可以使用github.com/shadkam/recentmost
user3392225 2014年

4
难以置信。16个答案,大多数/所有人甚至都不尝试执行OP所指定的操作...
hmijail哀悼辞职者,2017年

代替-R开关之类的解决方案,我只是在这里看到大量内容。
neverMind9 '18

应该是本机功能。
neverMind9

Answers:


486

试试这个:

#!/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

这有点快。


132
您的“快速方法”还应该能够使用print0支持文件名中的空格甚至换行。这是我使用的方法:find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head 这对我来说还是很快。

20
在Mac OS X上,它不是GNU的统计信息,因此命令失败。您必须brew install coreutils使用gstat而不是使用stat
CharlesB 2013年

36
您不需要运行,stat因为find PATH -type f -printf "%T@ %p\n"| sort -nr可以完成工作。这样也更快。
2013年

4
我们可以以某种方式将@ user37078的评论转换为实际答案还是编辑原始答案吗?这似乎是“正确的方式” [tm]。
mnagel

6
在Mac OS X上,无需安装gstat或其他任何措施,您可以执行以下操作:find PATH -type f -exec stat -f "%m %N" "{}" \; | sort -nr | head
cobbzilla

198

要查找所有文件状态最近更改过N分钟的文件:

find -cmin -N

例如:

find -cmin -5


4
+1谢谢,非常有用。在Windows上使用GnuWin32 find运行。
Sabuncu 2014年

非常简洁 非常好!
兰迪·L

它比其他更复杂的解决方案要快
david.perez,2015年

20
真的很好,例如,您可以在最后50天的更改中使用“ find -ctime -50”。
Gorkem '16

1
要排除混乱,请使用sudo find -cmin -1 2>&1 |grep -v /proc/
Cees Timmerman,2016年

39

GNU Find(请参阅参考资料man find)具有一个-printf用于显示文件EPOC mtime和相对路径名的参数。

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

3
谢谢!这是足够快的唯一答案,可以在合理的时间内搜索非常广泛的目录结构。我通过输出,tail以防止在输出中打印数千行。
sffc

8
另一条评论:awk '{print $2}'当文件名带有空格时,该部分似乎会引起问题。这是一个使用的解决方案sed,它还显示路径以外的时间:find . -type f -printf '%T@ %Tc %P\n' | sort -n | tail | sed -r 's/^.{22}//'
sffc

3
我认为应该排序-rn
BojanDević15年

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)和回声$ {X#*“”}对我来说很好(给我一些东西到第一个空格)
David Goodwin

2
如果文件名跨越多行,则全部将不起作用。使用touch "lala<Enter>b"来创建这样的文件。我认为Unix实用程序设计在文件名方面存在很大缺陷。
水果

35

我缩短了光晕对这种单线的回答

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的答案可以解决该限制。
卡尔·韦尔贝斯特,2015年

@carlverbies最确实的是,大量文件将破坏slashdottir的解决方案。甚至基于xargs的解决方案都将很慢。user2570243的解决方案最适合大型文件系统。
斯特凡纳·古里科

IFS=$'\n'在处理文件名时无论如何都不安全:换行符是UNIX上文件名中的有效字符。保证仅NUL字符不会出现在路径中。
查尔斯·达菲

17

尝试这个

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

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

目前,对于名称中包含空格或其他特殊字符的文件来说,这是不安全的。如果尚不能满足您的需求,请写一条表扬。


1
晕:我喜欢您的回答,它可以很好地工作并打印出正确的文件。但是我没有帮助,因为我的案例中有太多的子级别。所以对于ls我会收到“参数列表太长”的信息,而xargs在这种情况下也无济于事。我会尝试其他的东西。
fredrik

在这种情况下,它会更加复杂,并且需要一些实际的程序。我将介绍一些Perl。
DanielBöhmer

1
我改用PHP解决了这个问题。递归函数,通过文件系统树下降并存储最近修改的文件的时间。
fredrik

11

此命令在Mac OS X上有效:

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

在Linux上,按照原始发布者的要求,请使用stat代替gstat

当然,此答案是user37078的出色解决方案,从评论提升为完整答案。我混合了CharlesBgstat在Mac OS X 上使用的见解。顺便说一下,我从MacPorts而不是自制软件获得coreutils

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

#!/bin/bash
# ls-recent: list files in a dir tree, most recently modified first
#
# Usage: ls-recent path [-10 | more]
# 
# Where "path" is a path to target directory, "-10" is any arg to pass
# to "head" to limit the number of entries, and "more" is a special arg
# 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优胜美地上;我收到错误消息:查找:ftsopen:没有这样的文件或目录
Reece

有趣。您键入了什么命令(带有参数)?该目录中文件的名称是什么?并且,如果您创建了自己的版本~/bin/ls-recent.sh,是否已仔细检查了脚本中的差异?
Jim DeLaHunt

10
对于那些不想在Mac OS X上安装任何软件的用户:find . -exec stat -f '%m%t%Sm %N' {} + | sort -n | cut -f2-
Jake

5

这篇文章中的perl和Python解决方案都帮助我在Mac OS X上解决了这个问题:https : //unix.stackexchange.com/questions/9247/how-to-list-files-sorted-by-modification-date-recursively -no-stat-command-avail

从帖子中引用:

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
    }'

蟒蛇:

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'

5

忽略隐藏的文件-具有良好且快速的时间戳

很好地处理文件名中的空格-并非您应该使用它们!

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

2017.01.28 07h00 Sat ./recent
2017.01.21 10h49 Sat ./hgb
2017.01.16 07h44 Mon ./swx
2017.01.10 18h24 Tue ./update-stations
2017.01.09 10h38 Mon ./stations.json

find通过以下链接可以找到更多丰富的信息。


3

我正在显示最新访问时间,您可以轻松地将其修改为最新的修改时间。

有两种方法可以做到这一点:


1)如果要避免全局排序(如果您拥有数千万个文件,这可能会很昂贵),则可以执行以下操作:(将自己置于要开始搜索的目录的根目录中)

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 

上面的方法打印文件名的访问时间逐渐更新,而最后打印的文件是文件的访问时间最新。您显然可以使用“ tail -1”获得最新的访问时间。


2)您可以找到以递归方式打印子目录中所有文件的名称,访问时间,然后根据访问时间和尾部最大的条目进行排序:

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

那里有...


3

我的.profile中有这个别名,我经常使用

$ alias | grep xlogs
xlogs='sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | sudo xargs ls -ltr --color | less -R'

因此,它可以满足您的需求(例外情况是不会遍历更改日期/时间的多个级别)-查找最新文件(在这种情况下为* .log和* .trc文件);它也只查找在前一天修改过的文件,然后按时间排序并通过更少的管道输出:

sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | sudo xargs ls -ltr --color | less -R

ps。请注意,我在某些服务器上没有root用户,但始终使用sudo,因此您可能不需要该部分。


这到底是“您要找的东西”吗?OP对他想要的内容写了很好的解释,而这完全忽略了它。
hmijail哀悼辞职者,2017年

感谢您指出这一点。您是正确的-此方法不会进入多个级别来获取更改日期/时间,它仅显示其中目录文件的日期/时间。编辑了我的答案。
塔加尔

1

您可以给printf命令尝试一下

%Ak文件的上次访问时间,以k指定的格式,即@' or a directive for the C strftime函数。下面列出了k的可能值;由于系统之间的“ strftime”不同,某些功能可能并非在所有系统上都可用。


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> <filename>


1

这是一个可以处理文件名的版本,该文件名也可能包含空格,换行符和glob字符:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printf 打印文件修改(EPOCH值),后跟一个空格和 \0终止的文件名。
  • sort -zk1nr 读取NUL终止的数据并将其数字反向排序

由于问题是用Linux标记的,所以我假设 gnu utils可用。

您可以使用以下方法进行管道传输:

xargs -0 printf "%s\n"

打印修改时间和文件名,这些文件名和文件名按修改时间(最近的优先)排换行。


1

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

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

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

0

这也可以通过bash中的递归函数来完成

令F为一个函数,它显示文件的时间,该时间必须按字典顺序可排序yyyy-mm-dd等,(取决于OS?)

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.