ファイル名グループの検索/検出

ファイル名グループの検索/検出

fs には次のファイルがあります。 PREFIX_GROUPNAME_OTHERNAMES[.txt|.*]

たとえば、

A_ABC_A.txt
A_ABC_B.txt
A_ABC_C.txt
A_XYZ_A.txt
A_XYZ_B.txt
A_XYZ_C.txt

いくつかの追加作業のためにグループ名を取得したいと思います。

$# command i'm looking for
result:
> ABC XYZ

名前構造はわかりますが、グループ名はわかりません。

アイデア(しかし非常に高価に見えます!(大きなリストから)):

  • すべてのファイルスキャン
  • 名前の分割、グループ名によるリストの生成
  • グループに戻る

find と awk おそらく tr が解決策を見つけるときに探しているようです。

編集する:

これは一意でないリストを提供します。

find ./ -iname '*.txt' | xargs -n 1 | cut -d '_' -f 2
> ABC
> ABC
> ABC
> XYZ
> XYZ
> XYZ

答え1

以下は、シェル文字列操作と標準ツールのみを使用してsort防止します。出力の解析lsまたはfind、次のことを行わないことをお勧めします。

for f in *.*; do gr=${f#*_};gr=${gr%_*}; printf "%s\n" "$gr"; done | sort -u

あなたの場合は正確に出力する必要があります。

ABC
XYZ

説明する:

  • 一致するすべてのファイル名を繰り返します*.*(言うように、すべてのファイル名をキャプチャするには「最小包括的」パターンでなければなりません)。
  • シェル文字列操作を使用すると、最初の項目より前のすべての項目が削除され、次の2番目のステップ_では最後の項目から始まるすべての項目が削除されます_
  • 次の方法で結果を出力しますprintf。 (Stéphane Chazelasが指摘したように、シェルにこのコマンドが欠けている可能性はほとんどありません。)

最終出力は唯一の出力ではありません。重複を排除するために出力をパイプしますsort -u

ノートあなたが言ったように、このパターンに一致するファイルが多い場合、forループパラメータのリストがシェルの内部制限を超える可能性があります。また、この方法はファイル名の特殊文字に関連する多くのトラップを防ぎますが、ファイル名に改行文字(多くのファイルシステムのファイル名に有効な文字)が含まれている場合、この方法は失敗することを意味しますprintfsort

答え2

そしてzsh

typeset -U groups=( **/*_*_*.*(Ne['REPLY=${${(s[_])REPLY:t}[2]}']) )
  • typeset -U groups=(...):一意のメンバーを持つgroups配列として定義されていますU
  • **/*_*_*.*:ファイル名の一番右、現在の作業ディレクトリ、またはその下に.sが1つ以上、2つ以上があります。_.
  • (Ne['code']):glob修飾子はglobをさらに修飾します。
  • NNulglob:一致するものがない場合は空になるように拡張します。
  • e['code']各グローブを1($REPLYin code)に拡張して変換します。
  • $REPLY:ttファイルのail(デフォルト名)。
  • ${(s[_])var}:分割_(次に2番目の操作を実行します[2])。

bash(GNUシェル)、GNU find、GNUを使用すると、awk次のことができます。

readarray -td '' groups < <(
  LC_ALL=C find . -name '.?*' -prune -o \
    -name '*_*_*.*' -printf '%f\0' |
    gawk -v RS='\0' -v ORS='\0' -F _ '!seen[$2]++ {print $2}'
)

これは、最初の2つの文字の間にどの文字または非文字があるかを想定しません_

どちらも隠しファイルと隠しディレクトリのファイルをスキップします。これを含めるには、Dinにglob修飾子を追加するか、zshin-name '.?*' -prune -oを削除しますfind

ファイルリストが大きい場合find- ベースファイルはリスト全体をメモリに保存しないため、メモリフレンドリーです。同様のアプローチをとることができますzsh

typeset -A seen=()
: **/*_*_*.*(Ne['! seen[${${(s[_])REPLY:t}[2]}]='])
groups=( ${(k)seen} )

¹このコードの終了ステータスによってファイルが選択されているかどうかも判断されますが、このコードは常にtrueを返します。

答え3

答えを得る間に解決策も見つけました。 @AdminBeeも言及したように:

findファイルシステムの膨大な結果リストでxargs検索パターンを制限できない場合(例: "* .txt")を使用することを選択できます。

for f in ./some/path/*.txt; do gr=${f#*_};gr=${gr%_*}; echo "$gr"; done | sort -u
> ABC
> XYZ

find ./ -iname '*.txt' | xargs -n 1 | cut -d '_' -f 2 | sort -u
> ABC
> XYZ

関連情報