ディレクトリツリーで最も古いファイルを見つけるためのシェルワンライナーを探しています。
ディレクトリツリーで最も古いファイルを見つけるためのシェルワンライナーを探しています。
回答:
これは動作します(Daniel Anderssonの提案を組み込むために更新されました):
find -type f -printf '%T+ %p\n' | sort | head -n 1
find
ファイル名に改行が含まれているため、最初の行が空であるため、空のスペースができます。
これはもう少し移植性があり、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だと推測しています。私のソリューションでソートの出力を先頭にパイプすると、同様のエラーが発生しない理由がわかりません。
head
、行を読み取った後に終了するコマンドであるため、パイプが「壊れる」と思います。sort
それについて文句を言わないように見えるので、あなたはエラーを受け取りませんがls
、他のケースではします。
xargs
呼び出す必要があるファイル名が非常に多い場合、これは壊れますls
。その場合、それらの複数の呼び出しのソートされた出力は、それらをマージする必要があるときに連結されます。
ls
最も古いファイルをそれと眼球、あなたのソリューションは、おそらくだろう引き起こし、コマンドラインの長さの制限をオーバーランls
複数回呼び出されます。あなたは間違った答えを得るでしょうが、あなたは決して知りません。
次のコマンドコマンドは、あらゆる種類の奇妙なファイル名で動作することが保証されています。
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が必要です。
ここで受け入れられた答えと他の人が仕事をしますが、非常に大きなツリーがある場合、それらはすべてファイルの束全体をソートします。
並べ替える必要なく、それらを一覧表示して最も古いものを追跡することができれば、より良いでしょう。
それが、私がこの代替ソリューションを思いついた理由です:
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}'
コマンドの説明:
実行する:
〜$ 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
、スクリプト作成には適していますが、ls
解決策を説明する前にその情報を追加することもよいでしょう。
lsを使用してください-マニュアルページにディレクトリの注文方法が記載されています。
ls -clt | head -n 2
-n 2は、出力で「合計」を取得しないためです。ファイルの名前だけが必要な場合。
ls -t | head -n 1
そして、通常の順序でリストが必要な場合(最新のファイルを取得する)
ls -tr | head -n 1
findを使用するよりもはるかに簡単で、高速で、堅牢です。ファイルの命名形式を気にする必要はありません。ほぼすべてのシステムでも動作するはずです。
find ! -type d -printf "%T@ %p\n" | sort -n | head -n1
sort -n
。
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列目、つまりファイル名を表示します。
find -type f -printf '%T+ %p\n' | sort | head -1