bash スクリプトを学習しており、for ループ内のネストされた grep を使用して結果をフィルタリングしようとしています。
sample filenames:
this-file_AAC.txt
this-other_file_AAAC.txt
yep-a-file_AAC.nfo
oops_another_one.reamde
そしてこれがスクリプトです:
shopt -s globstar nullglob dotglob
for file in ./**/*.{txt,reamde,nfo}; do
printf "input: ${file}\n"
if grep -qF _AAC $file; then
printf "output: ${file}\n"
# do stuff with $file
fi
done
含まれるすべてのファイルに操作が適用されるという考え方です_AAC
。この特定のケースでは、結果が正しいことを確認するために stdout に出力するだけです。
問題は、ステートメントを通過できるものが何もないことですif
。つまり、何がわからないのかがわかりません。機能するのは、そのprintf "input...
行だけです。
何が足りないのでしょうか?
答え1
最後のファイル名部分.../something_AACxyz
が のような名前のすべてのファイルを一致させたい場合は、その部分を glob パターン/ワイルドカードに直接追加するだけです。_AAC
_AAC
for file in ./**/*_AAC*.{txt,reamde,nfo}; do
ファイル名を任意の部分に表示したい場合は_AAC
、次のようにします (Bash の場合):
for file in ./**/*.{txt,reamde,nfo}; do
if [[ $file == *_AAC* ]]; then
echo "'$file' contains _AAC"
else
echo "'$file' has NO _AAC"
fi
done
ここでは、glob に一致するが . がないファイルも取得します_AAC
。
いずれにしても、Bash では、中括弧の展開の代わりにextglob
パターンを有効にして書き換えることもできます。@(...|...)
for file in ./**/*_AAC*.@(txt|reamde|nfo); do
中括弧の展開では複数の異なるグロブが生成されますが、 の場合は@(..)
1 つのグロブだけが生成されます。(つまり、*.{foo,bar}
は と同じです*.foo *.bar
) nullglob
set の場合は一致しないグロブは問題になりませんが、failglob
set を使用した場合は、一致しないグロブが 1 つあるだけで、コマンド全体が失敗します。
答え2
スクリプトには次のように書かれています:
if grep -qF _AAC $file; then
これは、「指定されたファイルの内容に$file
リテラル文字列が含まれている場合は_AAC
...」ブロックの内容を実行することを意味しますthen
。
_AAC
代わりに、変数の展開の結果の文字列内でを検索する場合$file
(つまり、ファイル名自体に _AAC が出現するかどうかを確認する場合)、 では次のようにbash
記述する必要があります。
if grep -qF _AAC <<< "$file"; then
または、@roaima が指摘しているように、検索するリテラル文字列をワイルドカード ファイル仕様に含めることができます。