単一のコマンドでサブディレクトリでファイルを検索し、ファイル名でソートする方法は?

単一のコマンドでサブディレクトリでファイルを検索し、ファイル名でソートする方法は?

一般的なルックアップを使用した結果find . ! -path "./build*" -name "*.txt":

./tool/001-sub.txt
./tool/000-main.txt
./zo/001-int.txt
./zo/id/002-and.txt
./as/002-mod.txt

並べ替えるときsort -n

./as/002-mod.txt
./tool/000-main.txt
./tool/001-sub.txt
./zo/001-int.txt
./zo/id/002-and.txt

ただし、希望の出力は次のとおりです。

./tool/000-main.txt
./zo/001-int.txt
./tool/001-sub.txt
./zo/id/002-and.txt
./as/002-mod.txt

これは、出力が次に基づいていることを意味します。ファイル名のみただし、フォルダ情報は出力の一部として保持する必要があります。

編集する: サブディレクトリ構造に複数のレベルを含めることができるため、例はより複雑になります。

答え1

最後のフィールド(フィールド区切り文字と見なされる)に基づいてソートする必要があります/。残念ながら、フィールド数が変更された場合(sort -k負の値しか取れない場合)、これを行うことができるツールは思い出されません。

この問題を解決するには、装飾 - 並べ替え - 装飾キャンセルを実行する必要があります。つまり、ファイル名を取得して先頭に配置し、フィールド区切り記号を付けてソートし、最初の列とフィールド区切り文字を削除します。

find . ! -path "./build*" -name "*.txt" |\
    awk -vFS=/ -vOFS=/ '{ print $NF,$0 }' |\
    sort -n -t / |\
    cut -f2- -d/

このawkコマンドは次のことを意味します。フィールド区切り記号 FSに設定されます/。これはフィールドの読み方に影響します。これ出力フィールド区切り記号 OFSまた/、;に設定すると、レコードの印刷方法に影響します。次のステートメントは、最後の列(NFレコードのフィールド数なので、最後のフィールドのインデックスでもある)とレコード全体(レコード全体$0)を印刷するように指示します。その間にOFSを追加してください。その後、リストがsort編集され、/フィールド区切り文字として扱われます。レコード内のファイル名が最初にあるので、それに応じてソートされます。次に、cutフィールド 2 を最後まで印刷し、/フィールド区切り記号として再度処理します。

答え2

"-printf"ファイルを使用して名前とパスを出力し、名前でソートし、最後のステップで名前を切り捨てます。 「###」は切り抜きを助けるための表示に過ぎません。

find -name "*.txt" -printf "%f###%p\n" | sort -n | sed 's/.*###//'

%fはファイル名を印刷し、%pはフルパスを印刷します。

findコマンドを単純化して1行にしました。もちろんこの部分はそのままにしてください! -path "./build*"

答え3

zsh ≥4.3.10から:

print -l -- **/*.txt~build*(oe\''REPLY=${REPLY:t}'\')
  • **/*.txt*.txt現在のディレクトリとそのサブディレクトリと一致します。再帰的
  • ~build* 入らないようにするbuild*テキストが(like)で! -path './build*'始まるコンテンツと一致します。 (setopt extended_globまず必要です。)
  • (oe\''…'\')並べ替えグローバル予選REPLY=…返される文字列で並べ替える文字列を設定します。
  • ${REPLY:t}~である基本名(「尾」)パスです。

関連情報