web-dev-qa-db-ja.com

スクリプトを実行するためにパターンに一致するファイルがあるかどうかをテストする

特定のパターンに一致するファイルがあるかどうかをテストするifステートメントを記述しようとしています。ディレクトリにテキストファイルがある場合は、所定のスクリプトを実行する必要があります。

私のコードは現在:

if [ -f /*.txt ]; then ./script fi

いくつかのアイデアを教えてください。ディレクトリに.txtがある場合にのみ、スクリプトを実行します。

29
user40952
[ -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 nullglobbash固有ですが、ksh93zshyashtcshなどのシェルには同等のステートメントがあります。

ディレクトリのコンテンツを読み取ることによってこれらのファイルを見つけることに注意してください。これらのファイルにまったくアクセスしようとしないため、lsstat onなどのコマンドを呼び出すソリューションよりも効率的です。シェルによって計算されたファイルのリスト。

標準のshと同等のものは次のとおりです。

set -- [*].txt *.txt
case "$1$2" in
  ('[*].txt*.txt') ;;
  (*) shift; script "$@"
esac

問題は、BourneシェルまたはPOSIXシェルでは、パターンが一致しない場合、それ自体に拡張されることです。したがって、*.txt*.txtに展開された場合、それがディレクトリに.txtファイルがないか、または*.txtというファイルが1つあるためかはわかりません。 [*].txt *.txtを使用すると、2つを区別できます。

39

いつでもfindを使用できます:

find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script

説明:

  • find .:現在のディレクトリを検索
  • -maxdepth 1:サブディレクトリを検索しません
  • -type f:通常のファイルのみを検索
  • name "*.txt".txtで終わるファイルを検索します
  • 2>/dev/null:エラーメッセージを /dev/null にリダイレクトします
  • | grep -q .:任意の文字のgrep。文字が見つからない場合はfalseを返します。
  • && ./script:前のコマンドが成功した場合にのみ./scriptを実行します( &&
11
terdon

可能な解決策は、Bashビルトインcompgenです。このコマンドは、グロビングパターンの可能なすべての一致を返し、ファイルが一致したかどうかを示す終了コードがあります。

compgen -G "/*.text" > /dev/null && ./script

より速い解決策を探しているときに、私はこの質問を見つけました。

8
Daniel Böhmer

ここにそれを行うためのワンライナーがあります:

$ 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で広くカバーされています:

7
slm

Chazelasが指摘するように、ワイルドカード拡張が複数のファイルと一致する場合、スクリプトは失敗します。

ただし、回避策として(あまり気に入らなくても)使用するトリックがあります。

PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi

使い方?

ワイルドカード展開はファイル名の配列と一致します。いくつかある場合は最初のファイル名を取得し、それ以外の場合は一致しない場合はnullを取得します。

4
Pei Z

単純なように:

cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi

wc -lは、展開されたワイルドカードの行をカウントします。

1
Kim Johansson

私は以前の配列ソリューションが好きですが、それは多数のファイルで無駄になる可能性があります。シェルは配列を構築するために大量のメモリを使用し、最初の要素のみがテストされます。

ここに私が昨日テストした別の構造があります:

$ 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」を削除しました。)

0
Charlie