web-dev-qa-db-ja.com

Bash if条件で、単純なワイルドカード式に一致するファイルが存在するかどうかを確認するにはどうすればよいですか?

愚かに、私はスクリプトの一部としてこのような条件を使用していました:

if [ $(ls FOO* 2> /dev/null) ] # if files named "FOO*" were downloaded 
then
    echo "Files found" 
    # ... process and email results
else
    echo "Not found"
    # ... email warning that no files were found (against expectations)
fi

これはゼロと1つのファイルという名前のFOO*で機能しますが、-複数ある場合は失敗しますログから、これに起因するいくつかの異なるエラーメッセージが見つかりました。

[: FOO_20131107_082920: unary operator expected
[: FOO_20131108_070203: binary operator expected
[: too many arguments

私の質問は:Bash if条件で、名前が1つ以上のファイルで始まるかどうかを確認する正しい方法は何ですかFOOは存在しますか?

GNU bash、バージョン4.2.25(1)-release(x86_64-pc-linux-gnu)

7
Jonik

これは、lsのコマンド置換が空白を出力し、最終的に[に渡される前にWord分割されるためです。壊れにくい方法は、ファイルを配列に入れ、配列に少なくとも1つのメンバーがあることを確認することです。

shopt -s nullglob

files=( FOO* )
if (( ${#files[@]} )); then
    # there were files
fi

これが機能するのは、値が0でない場合にデフォルトで((がtrueを返し、${#files[@]}が配列内のアイテム数を取得するためです(グロブに一致するファイルがある場合は、> 0になります)。

nullglobが設定されていない限り、次のようなこともできます。

if ls FOO* >/dev/null 2>&1; then
    # there were files
fi

これはlsの終了コードをチェックするだけです。存在しないファイル名を渡した場合は1になります(リテラルFOO*、何も一致しない場合(もちろん、あなたが悪でなければ) FOO*という名前のファイルがあり、その場合は0が返されます:-)))。

これらはどちらもディレクトリと一致することに注意してください。通常のファイルのみを照合する場合は、それをテストする必要があります。

for file in FOO*; do
    if [[ -f $file ]]; then
        # file found, do some stuff and break
        break
    fi
done
16
Chris Down

上記の複数行の回答は、1行のソリューションに対する私の欲求に適合せず、環境を変更しませんでした。これはあなたのために働くかもしれない一般的なワンライナーです:

echo $(ls FOO* 2>/dev/null | wc -w)

/ dev/nullは、ファイルがない場合にlsがエラーをスローするためです。これは単にlsを無視し、「単語」(実際にはファイル名)の数に基づいて見つかったファイルの数を数えます。単純なifステートメントで使用できます。

if [ 0 -lt $(ls FOO* 2>/dev/null | wc -w) ]; then
 echo "Some FOO is there."
fi

このアプローチには、一致するファイルが存在するかどうかをチェックするだけでなく、オプションでいくつ一致するファイルが存在するかをチェックするという柔軟性があります(0および/または-lt)そして、その上でさらに先へ進みます。

ワイルドカードまたはスペースが含まれているファイル/フォルダーに対してはチェックしていません。上記の方法では、特にスペースで誤ったカウントが発生するのではないかと思います。これは、0を超える正確なカウントが必要な場合にのみ問題となります。これを試みた1つの修正は、$(ls -l FOO* 2>/dev/null | wc -l)を使用することです。ホワイトスペースをもっと受け入れる。

3
intech_guy

caseを使用する別のオプションがあります。

FILES=FOO*
FILES=$(echo $FILES)
case $FILES in FOO\*) echo "Not found" ;; esac

これは、テストする引数が1つだけであることを想定しているため機能します。

0
xae