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


29

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

現在の私のコード:

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

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


3
「ディレクトリ」が正しいはず/ですか?また、前にセミコロンがありませんfi
depquid

私が遭遇した最もクリーンで堅牢なソリューションは、stackoverflowでここでfind説明したように使用することです。
ジョシュアゴールドバーグ

回答:


39
[ -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特定の、しかし、シェルが好きksh93zshyashtcsh同等のステートメントを持っています。

注意は、それがディレクトリの内容を読み取ることによって、それらのファイルを見つけること、それは試してみて、解決策よりも、それをより効率的にそのすべてでそれらのファイルにアクセスしないようなコール・コマンド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です。
ダニエルベーマー

@DanielBöhmer [ -f /*.txt ]は間違ってい3425ますが、94非隠しtxtファイルであるファイルを含むディレクトリでの私のテストでは、(私の場合は10000回繰り返されると両方とも20.5秒)のcompgen -G "*.txt" > /dev/null 2>&1ように高速に見えますset -- *.txt; [ "$#" -gt 0 ]
ステファンシャゼル

11

常に使用できます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前のコマンドが成功した場合にのみ実行する(&&

2
findファイルの検索に問題がある場合のみfalseを返します。ファイルが見つからない場合はfalseを返しません。出力をパイプして、grep -q .何かを見つけるかどうかを確認します。
ステファンシャゼル

@StephaneChazelasあなたはもちろん非常に正しいです。しかし、奇妙なことに、私はそれをテストしましたが、うまくいくようでした。それはもうないので、何か奇妙なことをしたに違いありません。「ファイルの検索で問題が発生する」のはいつですか?
テルドン

@terdon。一部のディレクトリにアクセスできない場合、I / Oエラー、またはそれが行うシステムコールによって返されるエラーなど。その場合は、後に試してくださいchmod a-x .
ステファンシャゼラス

8

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

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

しかし、より高速なソリューションを探しているときにこの質問を見つけました。


1
良い発見!マルチバイトロケールを使用している場合は、で少し改善できますLC_ALL=C compgen -G "*.txt" > /dev/null
ステファンシャゼル

7

これを行う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が呼び出されます。

statから結果を返す

# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1

# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0

この質問は、stackoverflowで幅広くカバーされています。


1
なぜ非標準を使用しstatたときにls -d同じことを行うことができますか?
ステファンシャゼラス

ls -dディレクトリをリストすると思いましたか?ls -d *.plたとえば、ファイルを含むディレクトリを一覧表示しようとしたときに機能しなかったようです。
slm

&&by の左側のステートメントを置き換えることができls *.txt、それも機能します。/dev/null@slmの提案に従って、stdoutとstderrを必ず送信してください。
unxnut

1
を使用ls *.txtし、ディレクトリ内にファイルが存在しない場合、これはを返しますが$? = 2、これはif thenで引き続き動作しますが、これはを選択statする理由の1つでしたls。成功には0、失敗には1が必要でした。
slm

ls -dコンテンツの代わりにディレクトリをリストすることです。だから、ls -dちょうどんlstatGNUは同じように、ファイルにstatありません。ゼロ以外の終了ステータスコマンドが失敗時に返すものはシステム固有であり、それらを想定することはほとんど意味がありません。
ステファンシャゼル

4

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

ただし、回避するために私が使用するトリックがあります(私はそれがあまり好きではありません)。

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

使い方?

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


IMOこれはここで最も悪い答えです。基本的な機能が言語に欠けているかのように、それらはすべて非常に恐ろしいように見えます。
プラグウォッシュ

@plugwashは意図的なものです... * nixシェルスクリプトには、基本的なフロー制御とその他のいくつかのオッズと終了がありますが、結局のところ、仕事は他のコマンドを結合することです。bashがダメなら...それは、
それから

2
それは間違ったロジックです(引用符がありません)。最初に一致したファイルが通常のファイルであるかどうかを確認します。非正規ファイルの場合もあり.txtますが、タイプがregularの他のファイルがいくつかある場合があります。たとえば、後に試してくださいmkdir a.txt; mkfifo b.txt; echo regular > c.txt
ステファンシャゼル

1

単純なように:

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

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


1
Downvote:これは、少量のコードで驚くほど多くの初心者アンチパターンを収集します。出力解析lsしないでください。また$?if既に直接実行しているため、直接検査することはほとんどありません。また、何かが起こったかどうかを確認するwcため使用することは、同様に誤った方向に向けられます。
トリプリー

0

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

昨日テストした代替構造は次のとおりです。

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


-2

if句を使用する場合は、カウントを評価します。

if (( `ls *.txt 2> /dev/null|wc -l` ));then...
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.