关于bash:如何递归查找目录中最新修改的文件?

How to recursively find the latest modified file in a directory?

在执行递归调用时,ls似乎对文件排序不正确:

1
ls -altR . | head -n 3

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


1
2
find . -type f -printf '%T@ %p
'
| sort -n | tail -1 | cut -f2- -d""

对于一棵大树来说,sort可能很难将所有的东西都保存在内存中。

%T@给你修改时间,比如unix时间戳,sort -n按数字排序,tail -1取最后一行(最高时间戳),cut -f2 -d""从输出中去掉第一个字段(时间戳)。

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

我漏掉了复数的部分,如果你想要更多的文件,只需要增加尾部的参数。


下面是@plundra的答案,下面是BSD和OS X版本:

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


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

1
2
3
4
5
6
7
8
9
find . -type f -printf"%T@\0%p\0" | awk '
    {
        if ($0>max) {
            max=$0;
            getline mostrecent
        } else
            getline
    }
    END{print mostrecent}'
RS='\0'

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

我使用了nul字符(即),因为理论上,文件名可以包含任何字符(包括空格和换行符),但是。

如果系统中没有这样的病理文件名,也可以使用换行符:

1
2
3
4
5
6
7
8
9
10
11
12
find . -type f -printf"%T@
%p
"
| awk '
    {
        if ($0>max) {
            max=$0;
            getline mostrecent
        } else
            getline
    }
    END{print mostrecent}'
RS='
'

此外,这也适用于模型。


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

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

要显示文件名,请使用

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只返回上次修改的文件


这似乎工作得很好,即使是在子目录中:

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

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


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

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

结果如下:

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

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


这将给出一个排序列表:

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

通过在排序命令中放置"-r"来颠倒顺序。如果只需要文件名,请在"head"之前插入"awk"打印$11""


在Ubuntu13上,下面的操作可能要快一点,因为它颠倒了类型,使用"head"而不是"tail",减少了工作。要在树中显示11个最新文件:

找到。-类型F-printf"%t@%p"sort-n-r head-11 cut-f2-d"s e d-e's,^./,,'xargs l s-u-l

这将给出一个完整的ls列表,而不需要重新排序,并且省略了"find"在每个文件名上放置的恼人的"./"。

或者,作为bash函数:

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

尽管如此,大部分工作还是由普朗德拉的原始解决方案完成的。谢谢你。


我一直在使用类似的东西,以及最近修改过的文件的顶级列表。对于大型目录树,避免排序可能更快。如果只是Top-1最近修改的文件:

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

在一个包含170万个文件的目录中,我得到了3.4s中最新的一个,比使用sort的25.5s解决方案快7.5x。


我也面临同样的问题。我需要递归地查找最新的文件。查找大约需要50分钟。

下面是一个小脚本,可以更快地完成任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/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

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


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

1
ls -1t | head -1

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


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

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

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

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


我发现上面的命令很有用,但是对于我的情况,我需要查看文件的日期和时间,并且我对名称中有空格的几个文件有一个问题。这是我的工作方案。

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

我发现以下内容较短,且具有更多可解释的输出:

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

考虑到标准化的ISO格式日期时间的固定长度,词典编纂排序很好,我们不需要对排序使用-n选项。

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

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

以下命令适用于Solaris:

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


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

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

结果

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

1
2
3
4
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更多地跟随链接。


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

https://github.com/bucknerns/logtail网站

安装:

1
pip install logtail

用法:tails changed files

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

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

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

我喜欢这个,它比较短:

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