簡體   English   中英

如何遞歸查找並列出具有子目錄和時間的目錄中的最新修改文件

[英]How to recursively find and list the latest modified files in a directory with subdirectories and times

  • 操作系統:Linux

  • 文件系統類型: ext3

  • 首選解決方案:Bash(腳本/單行)、Ruby 或 Python

我有幾個目錄,其中有幾個子目錄和文件。 我需要列出所有這些目錄,這些目錄的構建方式使得每個一級目錄都列在其中最新創建/修改文件的日期和時間旁邊。

為了澄清,如果我觸摸一個文件或修改它的內容幾個子目錄級別,該時間戳應該顯示在第一級目錄名稱旁邊。 假設我有一個結構如下的目錄:

./alfa/beta/gamma/example.txt

我修改了文件example.txt的內容,我需要以人類可讀的形式顯示在一級目錄alfa旁邊的時間,而不是紀元。 我已經使用 find、 xargssort等嘗試了一些事情,但是當我創建/修改文件向下幾級時,我無法解決“alfa”的文件系統時間戳不會改變的問題。

試試這個:

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

這有點快。

要查找N分鍾前最后更改文件狀態的所有文件:

find -cmin -N

例如:

find -cmin -5

使用-ctime而不是-cmin幾天:

find -ctime -3

在 FreeBSD 和 MacOS 上:您還可以使用-ctime n[smhdw]表示秒、分鍾、小時、天和周。 如果未提供單位,則默認天數。

例子:

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

我縮短了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";

嘗試這個:

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

它使用find從目錄中收集所有文件,使用ls按修改日期排序列出它們,使用head選擇第一個文件,最后使用stat以漂亮的格式顯示時間。

目前,名稱中包含空格或其他特殊字符的文件是不安全的。 如果它還不能滿足您的需求,請寫一個表揚。

此命令適用於 Mac OS X:

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

在 Linux 上,正如原始發帖人所要求的,使用stat而不是gstat

這個答案當然是user37078的出色解決方案,從評論提升為完整答案。 我混合了 CharlesB 在 Mac OS X 上使用gstat的見解。順便說gstat ,我從MacPorts而不是Homebrew獲得了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

忽略隱藏文件 - 帶有漂亮和快速的時間戳

以下是如何在包含子目錄的目錄中查找和列出最新修改的文​​件。 隱藏文件被故意忽略。 而文件名中的空格處理得很好——不是你應該使用那些! 時間格式可以自定義。

$ 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

這篇文章中的 Perl 和 Python 解決方案都幫助我在 Mac OS X 上解決了這個問題:

如何遞歸地列出按修改日期排序的文件(沒有可用的 stat 命令!)

引用帖子:

珀爾:

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:

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'

這是一個適用於可能包含空格、換行符和全局字符的文件名的版本:

find . -type f -printf "%T@ %p\0" | sort -zk1nr
  • find ... -printf打印文件修改時間( Epoch 值),后跟空格和\0終止的文件名。
  • sort -zk1nr讀取 NUL 終止的數據並按數字倒序排序

由於這個問題是用 Linux 標記的,我假設GNU Core Utilities是可用的。

您可以通過以下方式對上述內容進行管道傳輸:

xargs -0 printf "%s\n"

打印修改時間和按修改時間排序的文件名(最近的第一個),由換行符終止。

我顯示的是最新的訪問時間,您可以輕松地修改它以進行最新的修改時間。

有兩種方法可以做到這一點:


  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. 您可以 find 遞歸打印子目錄中所有文件的名稱和訪問時間,然后根據訪問時間和尾部最大條目進行排序:

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

你有它...

我經常使用的 .profile 中有這個別名:

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

所以它會做你正在尋找的東西(除了它不會遍歷更改日期/時間多個級別) - 查找最新文件(在這種情況下為 *.log 和 *.trc 文件); 它也只查找在最后一天修改的文件,然后按時間排序並通過less管道輸出:

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

PS.:請注意,我在某些服務器上沒有 root 權限,但始終有sudo ,因此您可能不需要該部分。

這實際上應該執行 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

這列出了每個第一級目錄以及這些文件夾中最新文件的人類可讀時間戳,即使它位於子文件夾中,如

“我需要列出所有這些目錄,這些目錄的構建方式使得每個一級目錄都列在其中最新創建/修改文件的日期和時間旁邊。”

@anubhava 的答案很棒,但不幸的是不適用於 BSD 工具 - 即它不適用於默認安裝在 macOS find的 find ,因為 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,現在怎么辦?

快速打擊功能:

# 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

您還可以指定您自己的日期/時間格式作為第三個參數。

下面將返回一個時間戳字符串和具有最新時間戳的文件的名稱:

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>

對於那些面臨

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選項。

這也可以通過 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

你可以試試 find 的 printf ACTION

%Ak 文件的最后訪問時間,格式由 k 指定,可以是@' or a directive for the C 下面列出了 k 的可能值; 由於系統之間的“strftime”不同,其中一些可能並非在所有系統上都可用。

請在@anubhava 的答案中找到詳細信息

在 mac 我用這個

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

如果你想過濾一些文件,你可以使用 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 有一個單行腳本解決方案,用於如何遞歸地在多個目錄中查找最新修改的文​​件。 請在您的目標目錄中找到以下命令。

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

對於今天,grep 今天的日期或時間,如下面的命令中所述

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

對於普通的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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM