特定のパターンに一致するファイルがあるかどうかをテストするif
ステートメントを記述しようとしています。ディレクトリにテキストファイルがある場合は、所定のスクリプトを実行する必要があります。
私のコードは現在:
if [ -f /*.txt ]; then ./script fi
いくつかのアイデアを教えてください。ディレクトリに.txt
がある場合にのみ、スクリプトを実行します。
[ -f /*.txt ]
名前が/
で終わる.txt
に非表示ではないファイルone(および1つだけ)がある場合にのみtrueを返しますそのファイルが通常のファイルまたは通常のファイルへのシンボリックリンクである場合。
これは、ワイルドカードがコマンドに渡される前にシェルによって展開されるためです(ここでは[
)。
したがって、/a.txt
と/b.txt
がある場合、[
には次の5つの引数が渡されます:[
、-f
、/a.txt
、/b.txt
および]
。 [
は-f
に与えられた引数が多すぎると不平を言います。
*.txt
パターンが少なくとも1つの非表示でないファイル(通常かどうかにかかわらず)に展開されることを確認する場合:
shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
./script "$@" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"
shopt -s nullglob
はbash
固有ですが、ksh93
、zsh
、yash
、tcsh
などのシェルには同等のステートメントがあります。
ディレクトリのコンテンツを読み取ることによってこれらのファイルを見つけることに注意してください。これらのファイルにまったくアクセスしようとしないため、ls
やstat
onなどのコマンドを呼び出すソリューションよりも効率的です。シェルによって計算されたファイルのリスト。
標準のsh
と同等のものは次のとおりです。
set -- [*].txt *.txt
case "$1$2" in
('[*].txt*.txt') ;;
(*) shift; script "$@"
esac
問題は、BourneシェルまたはPOSIXシェルでは、パターンが一致しない場合、それ自体に拡張されることです。したがって、*.txt
が*.txt
に展開された場合、それがディレクトリに.txt
ファイルがないか、または*.txt
というファイルが1つあるためかはわかりません。 [*].txt *.txt
を使用すると、2つを区別できます。
いつでもfind
を使用できます:
find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script
説明:
可能な解決策は、Bashビルトインcompgen
です。このコマンドは、グロビングパターンの可能なすべての一致を返し、ファイルが一致したかどうかを示す終了コードがあります。
compgen -G "/*.text" > /dev/null && ./script
より速い解決策を探しているときに、私はこの質問を見つけました。
ここにそれを行うためのワンライナーがあります:
$ ls
file1.pl file2.pl
$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists
$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist
このアプローチでは||
および&&
bashの演算子。これらは「or」および「and」演算子です。
したがって、statコマンドが$?
が0の場合、最初のecho
が呼び出され、1を返す場合、2番目のecho
が呼び出されます。
# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1
# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0
この質問は、stackoverflowで広くカバーされています:
Chazelasが指摘するように、ワイルドカード拡張が複数のファイルと一致する場合、スクリプトは失敗します。
ただし、回避策として(あまり気に入らなくても)使用するトリックがあります。
PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi
使い方?
ワイルドカード展開はファイル名の配列と一致します。いくつかある場合は最初のファイル名を取得し、それ以外の場合は一致しない場合はnullを取得します。
単純なように:
cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi
wc -l
は、展開されたワイルドカードの行をカウントします。
私は以前の配列ソリューションが好きですが、それは多数のファイルで無駄になる可能性があります。シェルは配列を構築するために大量のメモリを使用し、最初の要素のみがテストされます。
ここに私が昨日テストした別の構造があります:
$ cd /etc; if [[ $(echo * | grep passwd) ]];then echo yes;else echo no;fi yes $ cd /etc; if [[ $(echo * | grep password) ]];then echo yes;else echo no;fi no
Grepからの終了値は、制御構造を通るパスを決定しているように見えます。これは、シェルパターンではなく正規表現でもテストされます。私のシステムのいくつかは、はるかに洗練された正規表現一致を可能にする「pcregrep」コマンドを備えています。
(私はこの回答を編集して、構文解析に対する上記の批判を読んだ後、コマンド置換の「ls」を削除しました。)