[英]How to recursively find and list the latest modified files in a directory with subdirectories and times
Operating system: Linux操作系统:Linux
Preferred solution: Bash (script/one-liner), Ruby, or Python首选解决方案:Bash(脚本/单行)、Ruby 或 Python
I have several directories with several subdirectories and files in them.我有几个目录,其中有几个子目录和文件。 I need to make a list of all these directories that is constructed in a way such that every first-level directory is listed next to the date and time of the latest created/modified file within it.
我需要列出所有这些目录,这些目录的构建方式使得每个一级目录都列在其中最新创建/修改文件的日期和时间旁边。
To clarify, if I touch a file or modify its contents a few subdirectory levels down, that timestamp should be displayed next to the first-level directory name.为了澄清,如果我触摸一个文件或修改它的内容几个子目录级别,该时间戳应该显示在第一级目录名称旁边。 Say I have a directory structured like this:
假设我有一个结构如下的目录:
./alfa/beta/gamma/example.txt
and I modify the contents of the file example.txt
, I need that time displayed next to the first-level directory alfa
in human readable form, not epoch.我修改了文件
example.txt
的内容,我需要以人类可读的形式显示在一级目录alfa
旁边的时间,而不是纪元。 I've tried some things using find, xargs
, sort
and the like, but I can't get around the problem that the filesystem timestamp of 'alfa' doesn't change when I create/modify files a few levels down.我已经使用 find、
xargs
、 sort
等尝试了一些事情,但是当我创建/修改文件向下几级时,我无法解决“alfa”的文件系统时间戳不会改变的问题。
Try this one:试试这个:
#!/bin/bash
find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head
Execute it with the path to the directory where it should start scanning recursively (it supports filenames with spaces).使用它应该开始递归扫描的目录的路径执行它(它支持带空格的文件名)。
If there are lots of files it may take a while before it returns anything.如果有很多文件,它可能需要一段时间才能返回任何内容。 Performance can be improved if we use
xargs
instead:如果我们改用
xargs
可以提高性能:
#!/bin/bash
find $1 -type f -print0 | xargs -0 stat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head
which is a bit faster.这有点快。
To find all files whose file status was last changed N minutes ago:要查找N分钟前最后更改文件状态的所有文件:
find -cmin -N
For example:例如:
find -cmin -5
Use -ctime
instead of -cmin
for days:使用
-ctime
而不是-cmin
几天:
find -ctime -3
On FreeBSD and MacOS : You can also use -ctime n[smhdw]
for seconds, minutes, hours, days, and weeks.在 FreeBSD 和 MacOS 上:您还可以使用
-ctime n[smhdw]
表示秒、分钟、小时、天和周。 Days is the default if no unit is provided.如果未提供单位,则默认天数。
Examples:例子:
# FreeBSD and MacOS only:
find . -ctime -30s
find . -ctime -15
find . -ctime -52w
GNU find(参见man find
)有一个-printf
参数,用于在 Epoch mtime 和相对路径名中显示文件。
redhat> find . -type f -printf '%T@ %P\n' | sort -n | awk '{print $2}'
I shortened Daniel Böhmer's awesome answer to this one-liner:我缩短了Daniel Böhmer 对这条单线的精彩回答:
stat --printf="%y %n\n" $(ls -tr $(find * -type f))
If there are spaces in filenames, you can use this modification:如果文件名中有空格,您可以使用此修改:
OFS="$IFS";IFS=$'\n';stat --printf="%y %n\n" $(ls -tr $(find . -type f));IFS="$OFS";
Try this:尝试这个:
#!/bin/bash
stat --format %y $(ls -t $(find alfa/ -type f) | head -n 1)
It uses find
to gather all files from the directory, ls
to list them sorted by modification date, head
for selecting the first file and finally stat
to show the time in a nice format.它使用
find
从目录中收集所有文件,使用ls
按修改日期排序列出它们,使用head
选择第一个文件,最后使用stat
以漂亮的格式显示时间。
At this time it is not safe for files with whitespace or other special characters in their names.目前,名称中包含空格或其他特殊字符的文件是不安全的。 Write a commend if it doesn't meet your needs yet.
如果它还不能满足您的需求,请写一个表扬。
This command works on Mac OS X:此命令适用于 Mac OS X:
find "$1" -type f -print0 | xargs -0 gstat --format '%Y :%y %n' | sort -nr | cut -d: -f2- | head
On Linux, as the original poster asked, use stat
instead of gstat
.在 Linux 上,正如原始发帖人所要求的,使用
stat
而不是gstat
。
This answer is, of course, user37078 's outstanding solution, promoted from comment to full answer.这个答案当然是user37078的出色解决方案,从评论提升为完整答案。 I mixed in CharlesB 's insight to use
gstat
on Mac OS X. I got coreutils from MacPorts rather than Homebrew , by the way.我混合了 CharlesB 在 Mac OS X 上使用gstat的见解。顺便说
gstat
,我从MacPorts而不是Homebrew获得了coreutils 。
And here's how I packaged this into a simple command ~/bin/ls-recent.sh
for reuse:以下是我如何将其打包成一个简单的命令
~/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
Here is how to find and list the latest modified files in a directory with subdirectories.以下是如何在包含子目录的目录中查找和列出最新修改的文件。 Hidden files are ignored on purpose.
隐藏文件被故意忽略。 Whereas spaces in filenames are handled well — not that you should use those!
而文件名中的空格处理得很好——不是你应该使用那些! The time format can be customised.
时间格式可以自定义。
$ 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
More find
galore can be found by following the link.可以通过以下链接找到更多
find
。
This is what I'm using (very efficient):这就是我正在使用的(非常有效):
function find_last () { find "${1:-.}" -type f -printf '%TY-%Tm-%Td %TH:%TM %P\n' 2>/dev/null | sort | tail -n "${2:-10}"; }
PROS:优点:
USAGE:用法:
find_last [dir [number]]
where:在哪里:
dir
- a directory to be searched [current dir] dir
- 要搜索的目录 [当前目录]number
- number of newest files to display [10] number
- 要显示的最新文件数 [10] Output for find_last /etc 4
looks like this: 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
Both the Perl and Python solutions in this post helped me solve this problem on Mac OS X:这篇文章中的 Perl 和 Python 解决方案都帮助我在 Mac OS X 上解决了这个问题:
How to list files sorted by modification date recursively (no stat command available!) 如何递归地列出按修改日期排序的文件(没有可用的 stat 命令!)
Quoting from the post:引用帖子:
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
}'
Python: Python:
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'
Here is one version that works with filenames that may contain spaces, newlines, and glob characters as well:这是一个适用于可能包含空格、换行符和全局字符的文件名的版本:
find . -type f -printf "%T@ %p\0" | sort -zk1nr
find ... -printf
prints the file modification time ( Epoch value ) followed by a space and \0
terminated filenames. find ... -printf
打印文件修改时间( Epoch 值),后跟空格和\0
终止的文件名。sort -zk1nr
reads NUL terminated data and sorts it reverse numerically sort -zk1nr
读取 NUL 终止的数据并按数字倒序排序As the question is tagged with Linux, I am assuming GNU Core Utilities are available.由于这个问题是用 Linux 标记的,我假设GNU Core Utilities是可用的。
You can pipe the above with:您可以通过以下方式对上述内容进行管道传输:
xargs -0 printf "%s\n"
to print the modification time and filenames sorted by modification time (most recent first) terminated by newlines.打印修改时间和按修改时间排序的文件名(最近的第一个),由换行符终止。
I'm showing this for the latest access time, and you can easily modify this to do latest modification time.我显示的是最新的访问时间,您可以轻松地修改它以进行最新的修改时间。
There are two ways to do this:有两种方法可以做到这一点:
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".
您显然可以使用“tail -1”获得最新的访问时间。
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:您可以 find 递归打印子目录中所有文件的名称和访问时间,然后根据访问时间和尾部最大条目进行排序:
Linux> \find . -type f -exec stat --printf="%X %n\n" {} \; | \sort -n | tail -1
And there you have it...你有它...
I have this alias in my .profile that I use quite often:我经常使用的 .profile 中有这个别名:
$ alias | grep xlogs
xlogs='sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | sudo xargs ls -ltr --color | less -R'
So it does what you are looking for (with exception it doesn't traverse change date/time multiple levels) - looks for latest files (*.log and *.trc files in this case);所以它会做你正在寻找的东西(除了它不会遍历更改日期/时间多个级别) - 查找最新文件(在这种情况下为 *.log 和 *.trc 文件); also it only finds files modified in the last day, and then sorts by time and pipes the output through less :
它也只查找在最后一天修改的文件,然后按时间排序并通过less管道输出:
sudo find . \( -name "*.log" -o -name "*.trc" \) -mtime -1 | sudo xargs ls -ltr --color | less -R
PS.: Notice I don't have root on some of the servers, but always have sudo , so you may not need that part. PS.:请注意,我在某些服务器上没有 root 权限,但始终有sudo ,因此您可能不需要该部分。
This should actually do what the OP specifies:这实际上应该执行 OP 指定的操作:
One-liner in Bash: 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
which gives output such as:它给出了输出,例如:
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
This lists each first-level directory with the human-readable timestamp of the latest file within those folders, even if it is in a subfolder, as requested in这列出了每个第一级目录以及这些文件夹中最新文件的人类可读时间戳,即使它位于子文件夹中,如
"I need to make a list of all these directories that is constructed in a way such that every first-level directory is listed next to the date and time of the latest created/modified file within it."
“我需要列出所有这些目录,这些目录的构建方式使得每个一级目录都列在其中最新创建/修改文件的日期和时间旁边。”
@anubhava's answer is great, but unfortunately won't work on BSD tools – ie it won't work with the find
that comes installed by default on macOS , because BSD find
doesn't have the -printf
operator. @anubhava 的答案很棒,但不幸的是不适用于 BSD 工具 - 即它不适用于默认安装在 macOS
find
的 find ,因为 BSD find
没有-printf
运算符。
So here's a variation that works with macOS + BSD (tested on my Catalina Mac), which combines BSD find
with xargs and stat
:所以这是一个适用于 macOS + BSD 的变体(在我的 Catalina Mac 上测试过),它结合了 BSD
find
与xargs和stat
:
$ find . -type f -print0 \
| xargs -0 -n1 -I{} stat -f '%Fm %N' "{}" \
| sort -rn
While I'm here, here's BSD command sequence I like to use, which puts the timestamp in ISO-8601 format当我在这里时,这是我喜欢使用的 BSD 命令序列,它将时间戳设置为ISO-8601 格式
$ find . -type f -print0 \
| xargs -0 -n1 -I{} \
stat -f '%Sm %N' -t '%Y-%m-%d %H:%M:%S' "{}" \
| sort -rn
(note that both my answers, unlike @anubhava's, pass the filenames from find
to xargs
as a single argument rather than a \0
terminated list, which changes what gets piped out at the very end) (请注意,与@anubhava 不同,我的两个答案都将文件名从
find
传递给xargs
作为单个参数而不是\0
终止列表,这会改变最后通过管道输出的内容)
And here's the GNU version (ie @anubhava's answer, but in iso-8601 format):这是 GNU 版本(即@anubhava 的答案,但采用 iso-8601 格式):
$ gfind . -type f -printf "%T+ %p\0" | sort -zk1nr
Related q: find lacks the option -printf, now what?相关问: find 缺少选项-printf,现在怎么办?
Quick Bash function:快速打击功能:
# 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"
}
Find the latest modified file in a directory:在目录中查找最新修改的文件:
findLatestModifiedFiles "/home/jason/" 1
You can also specify your own date/time format as the third argument.您还可以指定您自己的日期/时间格式作为第三个参数。
The following returns you a string of the timestamp and the name of the file with the most recent timestamp:下面将返回一个时间戳字符串和具有最新时间戳的文件的名称:
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
Resulting in an output of the form: <yy-mm-dd-hh-mm-ss.nanosec> <filename>
产生以下形式的输出:
<yy-mm-dd-hh-mm-ss.nanosec> <filename>
For those, who faced对于那些面临
stat: unrecognized option: format
when executed the line from Heppo's answer ( find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head
)当执行Heppo 答案中的行时(
find $1 -type f -exec stat --format '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head
)
Please try the -c
key to replace --format
and finally the call will be:请尝试使用
-c
键替换--format
最终调用将是:
find $1 -type f -exec stat -c '%Y :%y %n' "{}" \; | sort -nr | cut -d: -f2- | head
That worked for me inside of some Docker containers, where stat
was not able to use --format
option.这在一些 Docker 容器中对我有用,其中
stat
无法使用--format
选项。
This could be done with a recursive function in Bash too.这也可以通过 Bash 中的递归函数来完成。
Let F be a function that displays the time of file which must be lexicographically sortable yyyy-mm-dd, etc., (OS-dependent?)让 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, the recursive function that runs through directories: R,遍历目录的递归函数:
R(){ local f;for f in "$1"/*;do [ -d "$f" ]&&R $f||F "$f";done;}
And finally最后
for f in *;do [ -d "$f" ]&&echo `R "$f"|sort|tail -1`" $f";done
You may give the printf ACTION of find a try你可以试试 find 的 printf ACTION
%Ak File's last access time in the format specified by k, which is either
@' or a directive for the C
strftime' function.%Ak 文件的最后访问时间,格式由 k 指定,可以是
@' or a directive for the C
。 The possible values for k are listed below;下面列出了 k 的可能值; some of them might not be available on all systems, due to differences in `strftime' between systems.
由于系统之间的“strftime”不同,其中一些可能并非在所有系统上都可用。
Please find the details in @anubhava's answer请在@anubhava 的答案中找到详细信息
On mac I use this在 mac 我用这个
find . -type f -exec stat -f "%m %N" "{}" \; | sort -nr | perl -n -e '@a = split / /;print `ls -l $a[1]`' | vim -
if you want filter some files you can use grep with regexp ie如果你想过滤一些文件,你可以使用 grep 和正则表达式,即
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 has one-liner-script solution for, how to recursively find latest modified files in multiple directories. Bash 有一个单行脚本解决方案,用于如何递归地在多个目录中查找最新修改的文件。 kindly find below command with your target directories.
请在您的目标目录中找到以下命令。
ls -ltr $(find /path/dir1 /path/dir2 -type f)
and for today, grep today date or time as mentioned in below command对于今天,grep 今天的日期或时间,如下面的命令中所述
(ls -ltr $(find /path/dir1 /path/dir2 -type f)) |grep -i 'Oct 24'
For plain ls
output, use this.对于普通的
ls
输出,使用它。 There is no argument list, so it can't get too long:没有参数列表,所以不能太长:
find . | while read FILE;do ls -d -l "$FILE";done
And niceified with cut
for just the dates, times, and name:并用
cut
修饰日期、时间和名称:
find . | while read FILE;do ls -d -l "$FILE";done | cut --complement -d ' ' -f 1-5
EDIT : Just noticed that the current top answer sorts by modification date.编辑:刚刚注意到当前的最佳答案按修改日期排序。 That's just as easy with the second example here, since the modification date is first on each line - slap a sort onto the end:
这与这里的第二个示例一样简单,因为修改日期是每行的第一个 - 在末尾添加一个排序:
find . | while read FILE;do ls -d -l "$FILE";done | cut --complement -d ' ' -f 1-5 | sort
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.