if
特定のパターンに一致するファイルがあるかどうかをテストするステートメントを作成しようとしています。ディレクトリにテキストファイルがある場合、指定されたスクリプトを実行する必要があります。
現在の私のコード:
if [ -f /*.txt ]; then ./script fi
いくつかのアイデアを教えてください。.txt
ディレクトリ内にある場合にのみスクリプトを実行します。
if
特定のパターンに一致するファイルがあるかどうかをテストするステートメントを作成しようとしています。ディレクトリにテキストファイルがある場合、指定されたスクリプトを実行する必要があります。
現在の私のコード:
if [ -f /*.txt ]; then ./script fi
いくつかのアイデアを教えてください。.txt
ディレクトリ内にある場合にのみスクリプトを実行します。
回答:
[ -f /*.txt ]
あります場合にのみ、trueを返します1での(そして唯一の)非隠されたファイル/
に名前の端は.txt
、そのファイルが通常のファイルへの通常のファイルかシンボリックリンクであると場合。
これは、コマンドに渡される前にシェルによってワイルドカードが展開されるため[
です(こちら)。
あるかどうだから/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
シェルによって計算されたファイルのリストに。
sh
同等の標準は次のとおりです。
set -- [*].txt *.txt
case "$1$2" in
('[*].txt*.txt') ;;
(*) shift; script "$@"
esac
問題は、BourneまたはPOSIXシェルでは、パターンが一致しない場合、それ自体に展開されることです。したがって、に*.txt
展開した場合、ディレクトリにファイルが*.txt
ないためか、という.txt
ファイルが1つあるためかわかりません*.txt
。を使用[*].txt *.txt
すると、2つを区別できます。
[ -f /*.txt ]
と比較して非常に高速compgen
です。
[ -f /*.txt ]
は間違ってい3425
ますが、94
非隠しtxtファイルであるファイルを含むディレクトリでの私のテストでは、(私の場合は10000回繰り返されると両方とも20.5秒)のcompgen -G "*.txt" > /dev/null 2>&1
ように高速に見えますset -- *.txt; [ "$#" -gt 0 ]
。
常に使用できますfind
:
find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script
説明:
find
ファイルの検索に問題がある場合のみfalseを返します。ファイルが見つからない場合はfalseを返しません。出力をパイプして、grep -q .
何かを見つけるかどうかを確認します。
chmod a-x .
。
これを行う1つのライナーを次に示します。
$ 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で||
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で幅広くカバーされています。
stat
たときにls -d
同じことを行うことができますか?
ls -d
ディレクトリをリストすると思いましたか?ls -d *.pl
たとえば、ファイルを含むディレクトリを一覧表示しようとしたときに機能しなかったようです。
&&
by の左側のステートメントを置き換えることができls *.txt
、それも機能します。/dev/null
@slmの提案に従って、stdoutとstderrを必ず送信してください。
ls *.txt
し、ディレクトリ内にファイルが存在しない場合、これはを返しますが$? = 2
、これはif thenで引き続き動作しますが、これはを選択stat
する理由の1つでしたls
。成功には0、失敗には1が必要でした。
ls -d
コンテンツの代わりにディレクトリをリストすることです。だから、ls -d
ちょうどんlstat
GNUは同じように、ファイルにstat
ありません。ゼロ以外の終了ステータスコマンドが失敗時に返すものはシステム固有であり、それらを想定することはほとんど意味がありません。
Chazelasが指摘しているように、ワイルドカード展開が複数のファイルに一致する場合、スクリプトは失敗します。
ただし、回避するために私が使用するトリックがあります(私はそれがあまり好きではありません)。
PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi
使い方?
ワイルドカード展開はファイル名の配列と一致します。ある場合は最初のものを取得し、一致しない場合はnullを取得します。
.txt
ますが、タイプがregularの他のファイルがいくつかある場合があります。たとえば、後に試してくださいmkdir a.txt; mkfifo b.txt; echo regular > c.txt
。
私は以前の配列ソリューションが好きですが、それは大量のファイルで無駄になる可能性があります-シェルは大量のメモリを使用して配列を構築し、最初の要素のみがテストされます。
昨日テストした代替構造は次のとおりです。
$ 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」を削除しました。)
/
ですか?また、前にセミコロンがありませんfi
。