単一のコマンドでサブディレクトリ内のファイルを見つけてファイル名で並べ替える方法は?


9

を使用した通常の検索の結果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

つまり、出力はファイル名のみに基づいてソートされますが、フォルダー情報は出力の一部として維持する必要があります。

編集:サブディレクトリ構造に複数のレベルが含まれる場合があるため、例をより複雑にします。


2
:私はSOに尋ね、この質問を参照してくださいstackoverflow.com/questions/3222810/...
CAMH

@camh-可能であれば、UNIXコマンドのみを使用します。いずれにせよ、私の質問はあなたの質問とほとんど同じです。このスレッドに最適なソリューションを転送できますか(とにかく元のリンクを維持してください)。これをソリューションとしてマークできますか?
unode、

@Shawnがコメントで提案した変更を行う場合(の-printf代わりに使用awk)、それが最善の解決策だと思います。この方法を使用するように元の実装を作り直しました。
camh

回答:


9

最後のフィールドで並べ替える必要があります(/フィールド区切り文字と見なされます)。残念ながら、フィールド数が変化したときにこれを行うことができるツールは考えられません(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から最後までのみを印刷/し、フィールドセパレータとして扱います。


3
これはfind(1)を使用しているため、awkの部分をスキップして使用できます-printf '%f/%p\n'
camh

実際、セットアップは少し複雑です。可変サブディレクトリ深度が含まれます。この事実を反映するように質問を編集しました。最初はこれを含めなかったことをお詫びします。
unode

1
@Unode:Shawnのソリューションは、可変深度をうまく処理します。これは、この問題の標準的なソリューションです(マイナーバリエーションまで)。
Gilles「SO-悪をやめる」

4

最後の手順では、ファイル '-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*(など! -path './build*')で始まる一致を除外します。(setopt extended_glob最初に必要です。)
  • (oe\''…'\')は、ソートグロブ修飾子です。REPLY=…返す文字列からソートする文字列を作成します。
  • ${REPLY:t}パスのベース名(「テール」)です。

たくさんの連結された魔法。興味深いですが、sh構文に限定されています。+1
unode、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.