ディレクトリツリーで最も古いファイルを見つける方法


回答:


72

これは動作します(Daniel Anderssonの提案を組み込むために更新されました):

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

8
タイピングの減少:find -type f -printf '%T+ %p\n' | sort | head -1
ダニエルアンダーソン

1
findファイル名に改行が含まれているため、最初の行が空であるため、空のスペースができます。
林果皞

1
作成日または変更日を使用するかどうかを尋ねられますか?
-MrMesees

1
Linuxはファイルの作成日をどこにも保存しません[*]。これは修正日を使用します。[*]これは実際には正しくありません。ext4はiノードの作成日を保存しますが、システムコールを介して公開されないため、debugfsを使用して確認する必要があります。)
マリウスゲドミナス

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)を超える文字がある場合、正しい結果が得られません。また-print0、そうxargs -0でないため、POSIX準拠でもありません。

この問題に対するいくつかの解決策の概要を次に示します。ディレクトリ内で最新の(最新、最古、最古の)ファイルを見つけるにはどうすればよいですか?–グレッグのウィキ


これも機能しますが、xargs: ls: terminated by signal 13副作用としてエラーも発生します。私はそれがSIGPIPEだと推測しています。私のソリューションでソートの出力を先頭にパイプすると、同様のエラーが発生しない理由がわかりません。
マリウスゲドミナス

また、バージョンはメモリから簡単に入力できます。:-)
マリウスゲドミナス

はい、それは壊れたパイプです。これらすべてのコマンドの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と、ファイル名の1つに改行文字が含まれている場合でも、findの出力が理解できるようになります。

この-zスイッチは、sortとgrepの両方にヌルバイトのみを行末文字として解釈させます。headにはそのようなスイッチがないため、grep -m 1代わりに使用します(1回のみ)。

コマンドは実行時間順に並べられます(私のマシンで測定)。

  • 最初のコマンドは、すべてのファイルのmtimeを最初に人間が読める形式に変換してから、それらの文字列をソートする必要があるため、最も遅くなります。猫に配管すると、出力の色付けが回避されます。

  • 2番目のコマンドはわずかに高速です。まだ日付変換を実行している間sort -n、Unixエポックから経過した秒を数値的にソート()する方が少し速くなります。sedはUnixエポックからの秒数を削除します。

  • 最後のコマンドはまったく変換せず、最初の2つよりも大幅に高速である必要があります。findコマンド自体は最も古いファイルのmtimeを表示しないため、statが必要です。

関連manページ:findgrepsedsortstat


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
  • Awkは、カウンターをゼロに合わせて開始し(この質問ではオプションです)、古い日付を今日に設定し、YearMonthDayの形式にします。
  • 最初にメインループ
    • 6番目のフィールド、日付を取得し、年月日フォーマットを使用して、YearMonthDayに変更します(lsがこの方法で出力しない場合は、微調整する必要があります)。
    • 再帰を使用すると、/ directory / here:の形式で、すべてのディレクトリのヘッダー行があります。この行をpat変数に取り込みます。(最後の「:」を「/」に置き換えます)。また、ヘッダー行を有効なファイル行として使用しないように、$ 6を何も設定しません。
    • フィールド$ 6に有効な数値がある場合、その日付。古い日付と比較します。
    • 古いですか?次に、古い日付olddと古いファイル名oldfの新しい値を保存します。ところで、oldfは8番目のフィールドだけでなく、8番目から最後までです。これが、8番目からNF(終了)まで連結するループです。
    • 1ずつ進む
    • 結果を印刷して終了

実行する:

〜$ time ls -lRU "$ PWD" / * | awkなど

最も古い日付:19691231

ファイル:/home/.../.../backupold/.../EXAMPLES/how-to-program.txt

比較した合計:111438

実際の0m1.135s

ユーザー0m0.872s

sys 0m0.760s


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

スペースを含むファイルを受け入れるには、まだ週が必要です。すぐにやるよ。
博士ベコ

スペースを含むファイルのlsを解析するのは良い考えではないと思います。たぶんfindを使用しています。
ベコ博士

ツリー「/」全体で実行するだけです。費やした時間:合計比較:585744実2m14.017sユーザー0m8.181s sys 0m8.473s
Drベコ

ls出力はマシン向けではないため、スクリプトの使用は不適切です。出力のフォーマットは実装によって異なります。既に述べたようにfind、スクリプト作成には適していますが、ls解決策を説明する前にその情報を追加することもよいでしょう。
サンポサッララ

4

lsを使用してください-マニュアルページにディレクトリの注文方法が記載されています。

ls -clt | head -n 2

-n 2は、出力で「合計」を取得しないためです。ファイルの名前だけが必要な場合。

ls -t | head -n 1

そして、通常の順序でリストが必要な場合(最新のファイルを取得する)

ls -tr | head -n 1

findを使用するよりもはるかに簡単で、高速で、堅牢です。ファイルの命名形式を気にする必要はありません。ほぼすべてのシステムでも動作するはずです。


6
これは、ファイルが単一のディレクトリにある場合にのみ機能しますが、私の質問はディレクトリツリーに関するものでした。
マリウスゲドミナス14

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

2001年9月9日(Unixエポックから1000000000秒)より古いファイルがある場合、これは正しく機能しません。数値の並べ替えを有効にするには、を使用しますsort -n
デニス

:)これは私にファイルを見つけることができますが、それは、それは2番目のコマンドを実行せずにどのように古い参照するのは難しい
マリウス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' 2つの列に日付とファイル名を出力します。
  • sort | head -n1 最も古いファイルに対応する行を保持します。
  • echo $2 2列目、つまりファイル名を表示します。

1
スーパーユーザーへようこそ!これは質問に答えるかもしれませんが、なぜそうするのかについて何らかの説明を提供できれば、より良い答えになるでしょう。
DavidPostill

1
また、以前の(同一の)削除された回答の説明を何人かが求めました。
DavidPostill

答えるのが難しいのは何ですか?find ./search/dirname -type f -printf '%T +%h /%f \ n' | 並べ替える| head -n 1ファイルの時間とパスとして2つの列を表示します。最初の列を削除する必要があります。セットとエコー$ 2を使用して
ディマ

1
他の複数のユーザーから要求されたように、単にコマンドラインを貼り付けるのではなく、説明を提供する必要があります。
Ob1lan

1
これは受け入れられた答えとどう違うのですか?
ラムハウンド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.