パターンによるファイル名のグループ化と数の計算

パターンによるファイル名のグループ化と数の計算

特定の命名システムを使用するフォルダに多数のファイルがあります。次のように見えます。

my_file_A_a.txt
my_file_A_d.txt
my_file_A_f.txt
my_file_A_t.txt
my_file_B_r.txt
my_file_B_x.txt
my_file_C_f.txt
my_file_D_f.txt
my_file_D_g.txt
my_file_E_r.txt

次の結果を返すコマンドラインまたは一連のコマンド(一時ファイルが使用可能で書き込み権限がある)が必要です。

A: 4
B: 2
C: 1
D: 2
E: 1

多くのコマンドで実行できますが、ls -1 *A* | wc -l計算する「グループ」は何百ものあるため、時間がかかります。

また、各グループの名前は一意です。グループがあり、グループがAありますが、グループBはありませんAB

答え1

ファイル名が「うまくいく」と仮定します。つまり、改行文字は含まれておらず、次の組み合わせがls機能awkします。

ls -d my_file* | awk -F'_' 'NF==4{count[$3]++} END{for (i in count) printf "%s: %d\n", i, count[i]}'

lsこれにより、起動したプログラムのすべてのファイルを一覧表示するコマンドの出力がリダイレクトされます。プログラムは、asフィールド区切り文字を使用し、グループ番号を「配列インデックス」として使用する3番目のフィールドをチェックして、配列内の項目を追跡します。my_file*awkawk_count

最後に、各グループがどれだけ頻繁に発生するかについての概要を印刷します。

気づく

  • 正確に4つのフィールドを必要とすることで、完全に間違ったファイル名に対する「最小」保護が提供されます。この仮定は、例の_ファイル名のa、、、、、d...部分に含めることはできません。f
  • 出力が必ずしもカテゴリ名でソートされるわけではありません。ソート順序は、ループawk内で配列インデックスを移動する方法によって異なりますfor (i in count)。ソートが必要な場合は、他のパイプを追加できますsort。または、GNU Awkを使用している場合は、次のように構成設定を追加できます。
    BEGIN{PROCINFO["sorted_in"]="@ind_str_asc"}
    
    ルールの前にNF==4{...}。これにより、配列インデックスに基づいて配列が検索され、辞書(ASCII)順にソートされます。
  • これは、最初に説明した制限に適用され、ファイル名の構造が非常に単純であるためです。一般的に言えばそうです。解析出力は推奨されません。ls

答え2

for f in my_file_*_*.txt
do
    f="${f#my_file_}"
    printf "%s\n" "${f%%_*.txt}"
done |
sort |
uniq -c

ループforは、各ファイル名の形式を再指定してf先行my_file_と末尾_whatever.txtを削除し、出力をソートしてuniq各固有値の発生回数を計算するために使用します。

答え3

ワイルドカードを繰り返してから、bashの正規表現機能を使用してファイル名からフィールドを抽出して処理します。[[ 条件式の構成

unset collect
declare -A collect
for f in ./*_*_*_*.txt
do 
  [[ $f =~ [^_]+_+[^_]+_+([^_]+)_+[^_]+.txt ]] &&
  ((collect["${BASH_REMATCH[1]}"]++))
done

for group in "${!collect[@]}"
do
  printf '%s: %d\n' "$group" "${collect["$group"]}"
done

角かっこで囲まれた唯一のフィールドは、下線で区切られた3番目のフィールドです。キャプチャされると、collect連想配列()の値が増加します。

答え4

下線で区切られた4つのフィールドを含み、文字列で終わるファイル名は、.txt拡張ワイルドカードパターンと一致します+([!_])_+([!_])_+([!_])_+([!_]).txt。それぞれは、拡張正規表現+([!_])のように、アンダースコアではなく1つ以上の文字と一致します。[^_]+

サフィックス文字列を使用して、最初の2つのフィールドと最後のフィールドを削除して3番目のフィールドを抽出できます.txt

#!/bin/bash

shopt -s extglob nullglob

names=( +([!_])_+([!_])_+([!_])_+([!_]).txt )
names=( "${names[@]#+([!_])_+([!_])_}" )
names=( "${names[@]%_+([!_]).txt}" )

printf '%s\n' "${names[@]}" | sort | uniq -c

スクリプトは、ファイル名の 3 番目のフィールドに新しい行が含まれていないと仮定します。

質問のサンプルファイル名をテストします。

$ ls
list              my_file_A_f.txt   my_file_B_x.txt   my_file_D_g.txt
my_file_A_a.txt   my_file_A_t.txt   my_file_C_f.txt   my_file_E_r.txt
my_file_A_d.txt   my_file_B_r.txt   my_file_D_f.txt   script
$ ./script
   4 A
   2 B
   1 C
   2 D
   1 E

簡単なスクリプトでawkフィルタリングして目的の形式に変換できます。

$ ./script | awk '{ printf "%s: %d\n", $2, $1 }'
A: 4
B: 2
C: 1
D: 2
E: 1

名前が正しく機能している場合、つまり名前に改行文字が含まれていない場合は、スクリプトを少し単純化して使用できますcut

#!/bin/bash

shopt -s extglob nullglob

printf '%s\n' +([!_])_+([!_])_+([!_])_+([!_]).txt |
cut -d _ -f 3 | sort | uniq -c

関連情報