如何递归地找到并列出最新修改的文件与子目录和时间

  • 操作系统:Linux

  • 文件系统类型:ext3

  • 首选方案:Bash(脚本/一行程序)、Ruby或Python

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

为了说明这一点,如果我接触一个文件或修改它的内容向下几级子目录,该时间戳应该显示在第一级目录名旁边。假设我有一个这样的目录:

./alfa/beta/gamma/example.txt

我修改了文件example.txt的内容,我需要在第一级目录alfa旁边以人类可读的形式显示时间,而不是epoch。我已经尝试了一些使用find, xargssort之类的东西,但我无法解决“alfa”的文件系统时间戳不改变的问题,当我创建/修改文件的几个级别。

513967 次浏览

试试这个:

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

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

此时,对于名称中有空格或其他特殊字符的文件是不安全的。如果它还不能满足你的需求,就写一篇推荐信。

您可以尝试查找的printf ACTION

%Ak文件最后一次访问时间in 由k指定的格式, 要么是@' or a directive for the C strftime' 函数。列出了k的可能值 下面的; 其中一些可能无法全部使用 系统中,由于

.

.

.

详情请见@anubhava的回答

试试这个:

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

这样会快一点。

我将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";

对于普通的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

这也可以用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

我显示的是最新的访问时间,你可以很容易地修改它来做最新的修改时间。

有两种方法:


  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
    

    上面的方法打印访问时间逐渐更新的文件名,它打印的最后一个文件是具有最新访问时间的文件。显然,您可以使用“尾部-1”来获得最近的访问时间。

  2. 你可以递归地打印出子目录中所有文件的名称和访问时间,然后根据访问时间和最大条目的尾部进行排序:

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

现在你知道了……

GNU find(参见man find)有一个-printf参数,用于在Epoch mtime和相对路径名中显示文件。

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

查询文件状态在N分钟前被更改的所有文件。

find -cmin -N

例如:

find -cmin -5

使用-ctime代替-cmin:

find -ctime -3

在FreeBSD和MacOS上:您还可以使用-ctime n[smhdw]表示秒、分、小时、日和周。如果没有提供单位,Days是默认值。

例子:

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

快速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

您还可以指定自己的日期/时间格式作为第三个参数。

此命令适用于Mac OS X:

# EYZ0

在Linux上,正如最初的海报所要求的那样,使用stat而不是gstat

当然,这个答案是user37078的出色解决方案,从评论晋升为完整答案。我混合了CharlesB的见解,在Mac OS x上使用gstat。顺便说一下,我从MacPorts得到了coreutils,而不是家酿

下面是我如何将它打包成一个简单的命令~/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

我的.profile中有一个我经常使用的别名:

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

所以它做了你正在寻找的(除了它不遍历更改日期/时间多层)-寻找最新的文件(*.log和*.log)。本例为储税券文件);此外,它只找到在最后一天修改的文件,然后按时间排序,并通过管道输出:

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

注:注意我在一些服务器上没有root,但总是有sudo,所以你可能不需要这个部分。

下面返回一个字符串,包含时间戳和带有最近时间戳的文件名:

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

输出如下: # EYZ0 < / p >

下面是一个版本,它可以处理包含空格、换行符和glob字符的文件名:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printf打印文件修改时间(时代价值),后面跟着一个空格和\0终止的文件名。
  • sort -zk1nr读取以NUL终止的数据并对其进行反向排序

由于问题被标记为Linux,所以我假设GNU核心实用程序是可用的。

你可以用下面的管道:

xargs -0 printf "%s\n"

打印以换行符结束的按修改时间(最近的第一个)排序的修改时间和文件名。

忽略隐藏文件-使用nice &快速时间戳

下面介绍如何查找并列出带有子目录的目录中最新修改的文件。隐藏文件被故意忽略。然而文件名中的空格处理得很好-不是说你应该使用它们!时间格式可以自定义。

$ 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丰富可以通过以下链接找到。

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

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

这实际上应该做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

中所请求的,即使是在子文件夹中,也会列出这些文件夹中最新文件的人类可读时间戳的每个一级目录

"我需要列出所有这些目录是在 使每个一级目录都列在日期旁边的一种方法 其中最新创建/修改的文件的时间。"

对于那些面对的人

stat: unrecognized option: format

当执行一行从Heppo的回答 (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选项。

@anubhava的回答很棒,但不幸的是,它不能在BSD工具上工作——也就是说,它不能与find在macOS上默认安装一起工作,因为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选项,现在呢?

在mac上我用这个

find . -type f -exec stat -f "%m %N" "{}" \; | sort -nr | perl -n -e '@a = split / /;print `ls -l $a[1]`' | vim -

如果你想过滤一些文件,你可以使用grep与regexp,即。

find . -type f -exec stat -f "%m %N" "{}" \; | sort -nr | grep -v -E \.class$ | perl -n -e '@a = split / /;print `ls -l $a[1]`' | vim -

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

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

对于今天,grep今天的日期或时间如下面的命令所述

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