如何在目录树中找到最早的文件


Answers:


72

这项工作(已更新,以纳入丹尼尔·安德森的建议):

find -type f -printf '%T+ %p\n' | sort | head -n 1

8
减少打字的时间:find -type f -printf '%T+ %p\n' | sort | head -1
Daniel Andersson

1
find由于我的第一行是空的,因此我得到了空的空间,原因是我的文件名包含换行符。
林果皞2016年

1
请问这是否使用创建或修改日期?
MrMesees's

1
Linux不会在任何地方存储文件创建日期[*]。这使用修改日期。[*]这实际上是不正确的;ext4存储inode创建日期,但不会通过任何系统调用公开,因此您需要使用debugfs进行查看。)
Marius Gedminas

11

这有点可移植,并且由于它不依赖于GNU find扩展-printf,因此它也可以在BSD / OS X上运行:

find . -type f -print0 | xargs -0 ls -ltr | head -n 1

唯一的缺点是它的大小有所限制ARG_MAX(对于大多数较新的内核而言,这应该是无关紧要的)。因此,如果getconf ARG_MAX返回的字符多于我的系统(在我的系统上为262,144),则不会为您提供正确的结果。它也不符合POSIX,因为-print0xargs -0不兼容。

这里概述了一些其他解决方案:如何在目录中找到最新(最新,最早,最旧)文件?–格雷格的维基


这也可以,但是也会产生xargs: ls: terminated by signal 13错误。我猜那是SIGPIPE。我不知道为什么在将sort的输出传递到解决方案中时为什么没有出现类似的错误。
Marius Gedminas

您的版本也更容易从内存中键入。:-)
Marius Gedminas

是的,那是一条破损的管道。所有这些命令的GNU和BSD版本都没有,但是head我认为,一旦读取了一行并因此“破坏”了管道,该命令就会退出。您没有得到该错误,因为sort似乎没有抱怨它,但ls在另一种情况下却有抱怨。
slhck

4
如果有太多xargs需要多次调用的文件名,则此操作中断ls。在那种情况下,这些多个调用的排序输出在应该合并时最终被串联在一起。
妮可·汉密尔顿

2
我认为这比发布一个假设文件名不包含空格的脚本更糟糕。很多时候,这些文件会起作用,因为文件名没有空格。当它们失败时,您会得到一个错误。但这在实际情况下不太可能奏效,并且失败将不会被发现。在足够大的目录树上,您不仅不能限制ls它,而且要注意最旧的文件,您的解决方案可能超出命令行长度限制,从而导致ls多次调用。您将得到错误的答案,但永远不会知道。
妮可·汉密尔顿

11

保证以下命令命令可与任何种类的奇怪文件名一起使用:

find -type f -printf "%T+ %p\0" | sort -z | grep -zom 1 ".*" | cat

find -type f -printf "%T@ %T+ %p\0" | \
    sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'

stat -c "%y %n" "$(find -type f -printf "%T@ %p\0" | \
    sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //')"

使用空字节(\0)代替换行符(\n)可确保在文件名之一包含换行符的情况下,仍可以理解find的输出。

-z开关使sort和grep都仅将空字节解释为行尾字符。由于没有这样的换向开关,因此我们grep -m 1改用(只有一次)。

这些命令按执行时间排序(在我的机器上测量)。

  • 第一个命令将是最慢的,因为它必须先将每个文件的mtime转换为人类可读的格式,然后对这些字符串进行排序。用管道输送到猫可避免为输出着色。

  • 第二个命令稍快一些。尽管它仍然执行日期转换,但是对sort -n从Unix纪元开始经过的秒数进行数字排序()更快。sed删除自Unix时代以来的秒数。

  • 最后一个命令根本不执行任何转换,并且应该比前两个命令快得多。find命令本身不会显示最早文件的mtime,因此需要stat。

相关手册页:查找grepsed排序统计


5

尽管可以接受的答案和此处的其他答案都可以胜任,但是如果您有一棵大树,则它们都会对整个文件进行排序。

更好的情况是,我们可以只列出它们并跟踪最旧的,而无需进行排序。

那就是为什么我想出了这个替代解决方案:

ls -lRU $PWD/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($1,0,1)=="/") { pat=substr($1,0,length($0)-1)"/"; }; if( $6 != "") {if ( $6 < oldd ) { oldd=$6; oldf=pat$8; }; print $6, pat$8; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

我希望这可能对您有所帮助,即使这个问题有点老了。


编辑1:此更改允许使用空格分析文件和目录。它的速度足够快,可以将其发布到根目录/并找到有史以来最旧的文件。

ls -lRU --time-style=long-iso "$PWD"/* | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { gsub(/-/,"",$6); if (substr($0,0,1)=="/") { pat=substr($0,0,length($0)-1)"/"; $6="" }; if( $6 ~ /^[0-9]+$/) {if ( $6 < oldd ) { oldd=$6; oldf=$8; for(i=9; i<=NF; i++) oldf=oldf $i; oldf=pat oldf; }; count++;}} END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

命令说明:

  • ls -lRU --time-style = long-iso“ $ PWD” / *列出所有文件(*),长格式(l),递归(R),而无需快速排序(U),并将其通过管道传输到awk
  • 然后,Awk通过将计数器清零(此问题的可选内容)并设置最早的最早日期为今天(格式为YearMonthDay)来开始。
  • 主循环先
    • 抓取第6个字段,日期,将格式设置为Year-Month-Day,并将其更改为YearMonthDay(如果ls不以这种方式输出,则可能需要对其进行微调)。
    • 使用递归,所有目录的标题行都将以/ directory / here:的形式出现。将此行抓到pat变量中。(将最后的“:”替换为“ /”)。并将$ 6设置为空,以避免将标题行用作有效的文件行。
    • 如果字段$ 6具有有效数字,则为日期。将其与旧日期进行比较。
    • 年纪大了吗 然后为旧日期oldd和旧文件名oldf保存新值。顺便说一句,oldf不仅是第8场,而且是第8场到最后。这就是为什么循环从8号连接到NF(结束)的原因。
    • 将预付款计数一
    • 通过打印结果来结束

运行它:

〜$ time ls -lRU“ $ PWD” / * | awk等

最早的日期:19691231

文件:/ home /.../.../ backupold /.../ EXAMPLES / how-to-program.txt

总计比较:111438

真正的0m1.135s

用户0m0.872s

sys 0m0.760s


编辑2:相同的概念,更好的解决方案find用于查看访问时间%T与第一个printf一起使用,以用于修改时间%C用于状态更改)。

find . -wholename "*" -type f -printf "%AY%Am%Ad %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

编辑3:下面的命令波纹管使用修改时间,并且在找到越来越旧的文件也会打印增量进度,当您有一些不正确的时间戳(例如1970-01-01)时,这很有用:

find . -wholename "*" -type f -printf "%TY%Tm%Td %h/%f\n" | awk 'BEGIN {cont=0; oldd=strftime("%Y%m%d"); } { if ($1 < oldd) { oldd=$1; oldf=$2; for(i=3; i<=NF; i++) oldf=oldf " " $i; print oldd " " oldf; }; count++; } END { print "Oldest date: ", oldd, "\nFile:", oldf, "\nTotal compared: ", count}'

它仍然需要tweeking来接受带空格的文件。我会尽快做的。
Beco博士

我认为解析ls带有空格的文件不是一个好主意。也许使用查找。
Beco博士2015年

只需在整个树“ /”中运行它即可。花的时间:总计进行了比较:585744实际2m14.017s用户0m8.181s sys 0m8.473s
Beco博士

使用ls不利于脚本编写,因为其输出并不意味着要用于计算机,输出格式在实现中会有所不同。正如您已经说过find的,这对于脚本编写很有用,但是在介绍ls解决方案之前添加该信息也可能会很好。
Sampo Sarrala '16

4

请使用ls-手册页告诉您如何订购目录。

ls -clt | head -n 2

-n 2是,因此您不会在输出中得到“总计”。如果只需要文件名。

ls -t | head -n 1

并且如果您需要按正常顺序排列列表(获取最新文件)

ls -tr | head -n 1

它比使用find更容易,更快,更可靠-不必担心文件命名格式。它也应该适用于几乎所有系统。


6
仅当文件位于单个目录中,而我的问题是关于目录树时,此方法才有效。
Marius Gedminas 2014年

2
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1

如果文件的日期早于2001年9月9日(自Unix时代以来为1000000000秒),则此操作将无法正常工作。要启用数字排序,请使用sort -n
丹尼斯

这有助于我找到文件,但是如果不运行第二个命令就很难知道它有多旧:)
Marius Gedminas

0

看来,“最旧的”大多数人都认为您的意思是“最旧的修改时间”。根据对“最旧”的最严格的解释,这可能已得到纠正,但是如果您想要访问时间最久的那个,我将因此修改最佳答案:

find -type f -printf '%A+ %p\n' | sort | head -n 1

请注意%A+


-1
set $(find /search/dirname -type f -printf '%T+ %h/%f\n' | sort | head -n 1) && echo $2
  • find ./search/dirname -type f -printf '%T+ %h/%f\n' 在两列中显示日期和文件名。
  • sort | head -n1 保持对应于最旧文件的行。
  • echo $2 显示第二列,即文件名。

1
欢迎来到超级用户!尽管这可以回答问题,但是如果您可以提供解释为什么会这样做会更好。
DavidPostill

1
请注意,一些人还要求您对以前(相同的)已删除答案作一些解释。
DavidPostill

什么很难回答?查找./search/dirname -type f -printf'%T +%h /%f \ n'| 排序| head -n 1它显示两列作为文件的时间和路径。有必要删除第一列。使用set和echo $ 2
Dima

1
您应该提供解释,而不是仅按照其他几个用户的要求粘贴命令行。
Ob1lan 2015年

1
这与接受的答案有何不同?
Ramhound
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.