特定の命名システムを使用するフォルダに多数のファイルがあります。次のように見えます。
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*
awk
awk
_
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