グロブがbashで一致するかどうかをテストする


223

単一のファイルの存在を確認する場合は、test -e filenameまたはを使用してテストできます[ -e filename ]

私がグロブを持っていて、そのグロブと名前が一致するファイルが存在するかどうかを知りたいとします。globは0個のファイルに一致する(この場合は何もする必要がない)か、1つ以上のファイルに一致する(この場合は何かする必要がある)ことができます。グロブに一致があるかどうかをテストするにはどうすればよいですか?(一致がいくつあるかは気にしません。1つのifステートメントでループなしでこれを実行できれば最適です(それが最も読みやすいことがわかったからです)。

test -e glob*グロブが複数のファイルと一致する場合は失敗します。)


3
以下の私の答えは、他のすべての種類のハックアラウンドのように「明らかに正しい」と思います。それは永遠に存在し、「この特定の仕事のための意図されたツール」であるように見える1行のシェル構築です。ユーザーがここで受け入れられた回答を誤って参照することを心配しています。だれでも自由に修正してください。ここでコメントを取り下げます。私は間違ってそれから学ぶことができてとてもうれしいです。差がそれほど大きくない場合は、この問題は発生しません。
Brian Chrisman、2015

1
この質問に対する私のお気に入りの解決策は任意のシェル(非Bourneシェル)で機能するが、GNU findを必要とするfindコマンドと、明らかにバシズムであるcompgenコマンドです。残念ながら、両方の回答を受け入れることはできません。
Ken Bloom

注:この質問は、質問されてから編集されています。元のタイトルは「グロブがbashで一致するかどうかをテストする」でした。特定のシェル「bash」は、回答を公開した後、質問から削除されました。質問のタイトルを編集すると、私の回答が誤っているように見えます。誰かがこの変更を修正または少なくとも対処できることを願っています。
ブライアンクリスマン

回答:


178

Bash固有のソリューション:

compgen -G "<glob-pattern>"

パターンをエスケープします。そうしないと、事前に展開されて一致します。

終了ステータスは次のとおりです。

  • 不一致の場合は1、
  • 「1つ以上の一致」の場合は0

stdout、グロブに一致するファイルのリストです
これは、簡潔さと潜在的な副作用の最小化の観点から、最良のオプションだと思います。

UPDATE:使用例のリクエスト。

if compgen -G "/tmp/someFiles*" > /dev/null; then
    echo "Some files exist."
fi

9
これcompgenbash固有の組み込みコマンドであり、POSIX標準のUnixシェルで指定された組み込みコマンドの一部ではないことに注意してください。pubs.opengroup.org/onlinepubs/9699919799の pubs.opengroup.org/onlinepubs/9699919799/utilities/...はそのため、他のシェルへの移植性が懸念されるスクリプトでそれを使用しないでください。
Diomidis Spinellis

1
bashビルトインなしの同様の効果は、グロブに作用する他のコマンドを使用することであり、lsなどのファイルが一致しない場合は失敗するようif ls /tmp/*Files 2>&1 >/dev/null; then echo exists; fiです。グロブと一致してはならない、グロブと同じ名前のファイルがある場合は失敗しますが、その場合はおそらくより大きな問題が発生します。
Dewi Morgan

4
@DewiMorganこれはより簡単です:if ls /tmp/*Files &> /dev/null; then echo exists; fi
クレイブリッジ

の詳細についてはcompgen、「man bash〜と一緒に」を参照してくださいhelp compgen
el-teedee

2
ええ、それを引用するか、ファイル名のワイルドカードが事前に展開されます。compgen "dir / *。ext"
ブライアンクリスマン

169

nullglobシェルオプションは確かにバシズムです。

nullglob状態の退屈な保存と復元の必要性を回避するために、globを展開するサブシェル内でのみ設定します。

if test -n "$(shopt -s nullglob; echo glob*)"
then
    echo found
else
    echo not found
fi

移植性を向上させ、より柔軟なグロビングを行うには、findを使用します。

if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
    echo found
else
    echo not found
fi

明示的な-print -quitのアクションがために使用されている検索デフォルトの暗黙的なのではなく、-printように、アクションのfindは、それが検索条件に一致する最初のファイルを見つけるとすぐに終了します。多くのファイルが一致する場合、これはecho glob*またはよりも高速に実行ls glob*され、拡張されたコマンドラインがいっぱいになる可能性も回避されます(一部のシェルには4Kの長さ制限があります)。

場合検索はやり過ぎのように感じていると、おそらく試合へのファイルの数が少ない、使用スタット:

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi

10
findまさに正しいようです。シェルが展開を行っていない(そして展開されていないグロブを他のコマンドに渡していない)ため、特別なケースはありません(シェル間で移植可能です(ただし、使用するすべてのオプションがPOSIXで指定されているわけではありません))。ls -d glob*(以前に受け入れられた回答)最初の一致に到達すると停止するため。
ケンブルーム

1
この答えが必要とするかもしれないことに注意shopt -u failglobこれらのオプションは何とか矛盾するように見えるよう。
Calimo 2014

find解決策は、同様に無グロブ文字をファイル名と一致します。この場合、それが私が欲しかったものです。ただし、注意が必要なことです。
私たちは全員モニカです

1
他の誰かが私の答えを編集してそれを言うように決めたので、どうやら。
flabdablet 2017年

1
unix.stackexchange.com/questions/275637/…-maxdepth、POSIX検索のオプションを置き換える方法について説明しています。
ケン・ブルーム

25
#!/usr/bin/env bash

# If it is set, then an unmatched glob is swept away entirely -- 
# replaced with a set of zero words -- 
# instead of remaining in place as a single word.
shopt -s nullglob

M=(*px)

if [ "${#M[*]}" -ge 1 ]; then
    echo "${#M[*]} matches."
else
    echo "No such files."
fi

2
nullglob単一の結果がパターン自体と等しいかどうかを確認するのではなく、誤った「一致なし」セットの可能性を回避するため。一部のパターンは、パターン自体と完全に等しい名前に一致する場合があります(例a*ba?bまたは[a]。ではない)。
Chris Johnsen、

これは、グロブのような名前のファイルが実際に存在する可能性が非常に低い場合に失敗すると思います。(例えば誰かが走ったtouch '*py')、しかしこれは私を別の良い方向に向けます。
ケンブルーム2010年

私はこれが最も一般的なバージョンとして好きです。
ケンブルーム

そしてまた最短。1つの一致のみを期待している場合は"$M"、の省略形として使用できます"${M[0]}"。それ以外の場合は、配列変数にグロブ拡張が既にあるので、グロブを再拡張させるのではなく、リストとして他のものに渡すためのgtgです。
Peter Cordes、2015

いいね。あなたは(あまりバイトと産卵せずに、より迅速にMをテストすることができます[し、プロセスを)if [[ $M ]]; then ...
トビア

22

好き

exists() {
    [ -e "$1" ]
}

if exists glob*; then
    echo found
else
    echo not found
fi

これは、読みやすく効率的です(膨大な数のファイルがない限り)。
主な欠点は、見た目よりもはるかに微妙であり、長いコメントを付けざるを得ない場合があることです。
一致がある場合"glob*"は、シェルによって展開され、すべての一致がに渡されexists()、最初の一致がチェックされ、残りは無視されます。
一致しない場合、"glob*"に渡されexists()、そこにも存在しないことがわかります。

編集:誤検知がある可能性があります、コメントを参照してください


13
グロブが次のようなもので*.[cC]ある場合(そうではないcか、Cファイルである可能性がありますが、ファイルと呼ばれます*.[cC])、または最初のファイルがそこから展開された場合、たとえば、存在しないファイルまたはアクセスできないディレクトリ(を追加する方法|| [ -L "$1" ])。
ステファンChazelas 2013年

面白い。Shellcheckは、-e一致が0または1の場合、グロビングはでのみ機能することを報告します。これは複数のマッチに対して[ -e file1 file2 ]は機能しません。根拠と推奨される解決策については、github.com / koalaman / shellcheck / wiki / SC2144も参照してください。
Thomas Praxl

10

globfailが設定されている場合は、これをクレイジーに使用できます(実際には使用しないでください)。

shopt -s failglob # exit if * does not match 
( : * ) && echo 0 || echo 1

または

q=( * ) && echo 0 || echo 1

2
noopの素晴らしい使用法は失敗します。決して使われるべきではありません...しかし、本当に美しいです。:)
ブライアンクリスマン

買い物かごを括弧の中に入れることができます。その方法は、それが唯一のテストに影響します(shopt -s failglob; : *) 2>/dev/null && echo exists
flabdablet

8

test -eには、壊れたシンボリックリンクが存在しないと見なされるという不幸な警告があります。そのため、それらもチェックしたい場合があります。

function globexists {
  test -e "$1" -o -L "$1"
}

if globexists glob*; then
    echo found
else
    echo not found
fi

4
それでも、Stephane ChazelasがDan Blochの答えを指摘しているように、glob特殊文字が含まれているファイル名の誤検知は修正されません。(ヌルグロブでサルを除いて)。
Peter Cordes、2015

3
あなたは避けshoud -o-atest/ [。たとえば、ここで$1=ほとんどの実装で失敗します。[ -e "$1" ] || [ -L "$1" ]代わりに使用してください。
Stephane Chazelas

4

彼の考えに基づいて、MYYNの答えをいくらか単純化するために:

M=(*py)
if [ -e ${M[0]} ]; then
  echo Found
else
  echo Not Found
fi

4
閉じるが、一致する場合[a]、という名前のファイルは[a]あるが、という名前のファイルがない場合はどうでしょaうか。私はまだnullglobこれが好きです。一部の人はこれを独断的であると見なすかもしれませんが、合理的であるように完全に正しいかもしれません。
Chris Johnsen、

@ sondra.kinseyそれは間違っています。glob [a]aリテラルファイル名ではなく、一致する必要があります[a]
Tripleee

4

flabdabletの回答に基づいて、私にとっては、シェル自体にglob展開を残しながら、find自体を使用するのが最も簡単な(必ずしも最速ではない)ように見えます

find /some/{p,long-p}ath/with/*globs* -quit &> /dev/null && echo "MATCH"

またはのifように:

if find $yourGlob -quit &> /dev/null; then
    echo "MATCH"
else
    echo "NOT-FOUND"
fi

これは、statを使用してすでに提示したバージョンとまったく同じように機能します。どのように見つけることがstatよりも「簡単」かはわかりません。
flabdablet 2014

3
&>リダイレクションはバシズムであり、他のシェルでは静かに間違った動作をすることに注意してください。
flabdablet 2014

これはfind、glob内のパスを受け入れ、より簡潔なため(必要としない-maxdepthなど)、flabdabletの回答よりも優れているようです。また、グロブの一致が追加されるたびに追加のing statを実行しないため、彼の回答よりも優れているようですstat。これが機能しないコーナーケースに貢献できる人がいれば幸いです。
drwatsoncode 2016年

1
さらに検討した後、-maxdepth 0条件を追加する際の柔軟性が高まるため、追加します。たとえば、一致するファイルのみに結果を制限したいとします。私は試すかもしれませんがfind $glob -type f -quit、グロブがファイルと一致しなかった場合はtrueを返しますが、ファイルを含むディレクトリと(再帰的にでも)一致しました。対照的にfind $glob -maxdepth 0 -type f -quit、グロブ自体が少なくとも1つのファイルと一致した場合にのみtrueを返します。maxdepthグロブがディレクトリコンポーネントを持つことを妨げないことに注意してください。(FYI 2>で十分です。必要はありません&>
drwatsoncode

2
findそもそも使用するポイントは、シェルがグロブ一致の潜在的に巨大なリストを生成およびソートしないようにすることです。find -name ... -quit最大で1つのファイル名に一致します。スクリプトが、シェルが生成したグロブ一致のリストをに渡すことに依存している場合find、呼び出しをfind実行しても、不必要なプロセス起動オーバーヘッドしか発生しません。結果のリストが空でないかどうかを直接テストするだけで、より早く明確になります。
flabdablet

4

私はさらに別の解決策を持っています:

if [ "$(echo glob*)" != 'glob*' ]

これは私にとってはうまくいきます。見逃してしまうコーナーケースはありますか?


2
ファイルが実際に「glob *」という名前の場合を除いて機能します。
Ian Kelling 2016

グロブを変数として渡すために機能します-一致するものが複数ある場合、「引数が多すぎます」エラーが発生します。"$(echo $ GLOB)"が単一の文字列を返さないか、少なくとも単一の文字列として解釈されないため、引数が多すぎるエラー
DKebler

@DKebler:二重引用符で囲まれているため、単一の文字列として解釈されます。
user1934428

3

Bashでは、配列にグロブできます。グロブが一致しなかった場合、配列には既存のファイルに対応しない単一のエントリが含まれます。

#!/bin/bash

shellglob='*.sh'

scripts=($shellglob)

if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi

注:nullglob設定した場合scripts、空の配列になります。代わりに[ "${scripts[*]}" ]またはでテストする必要があり[ "${#scripts[*]}" != 0 ]ます。の有無にかかわらず動作する必要があるライブラリを作成しているnullglob場合は、

if [ "${scripts[*]}" ] && [ -e "${scripts[0]}" ]

このアプローチの利点は、glob操作を繰り返す必要がなく、操作したいファイルのリストが得られることです。


nullglobが設定されていて、配列が空の可能性があるのに、まだテストできないのは if [ -e "${scripts[0]}" ]...なぜですか?シェルオプションnounsetセットの可能性も考慮していますか?
johnraff 2018

@johnraff、はい、私は通常nounsetアクティブであると思います。また、ファイルの存在を確認するよりも、文字列が空でないことをテストする方が(わずかに)安くなる場合があります。とはいえ、グロブを実行したばかりであることを考えると、ディレクトリの内容はOSのキャッシュに新しいはずです。
Toby Speight

1

この嫌悪感はうまくいくようです:

#!/usr/bin/env bash
shopt -s nullglob
if [ "`echo *py`" != "" ]; then
    echo "Glob matched"
else
    echo "Glob did not match"
fi

おそらく、shではなくbashが必要です。

これは、nullglobオプションを使用すると、一致がない場合にglobが空の文字列に評価されるため、機能します。したがって、echoコマンドからの空でない出力は、グロブが何かと一致したことを示します。


使用する必要がありますif [ "`echo *py`" != "*py"]
yegle

1
というファイルがあった場合、これは正しく機能しません*py
ライアンC.トンプソン

で終わるファイルがない場合py`echo *py`はに評価され*pyます。
yegle

1
はい、ですが、という名前の単一のファイルがある場合にもそうなりますが*py、これは間違った結果です。
ライアンC.トンプソン

私が間違っている場合は修正してください。ただし、に一致するファイルがない場合*py、スクリプトは「Glob matching」とエコーしますか?
yegle

1

私はこの答えを見なかったので、私はそれをそこに出すと思いました:

set -- glob*
[ -f "$1" ] && echo "found $@"

1
set -- glob*
if [ -f "$1" ]; then
  echo "It matched"
fi

説明

の一致がない場合はglob*$1が含まれます'glob*'。ファイルが存在しない-f "$1"ため、テストは真ではありglob*ません。

なぜこれが代替案より優れているのか

これは、shおよび派生物(kshおよびbash)で機能します。サブシェルは作成されません。$(..)および`...`コマンドはサブシェルを作成します。プロセスを分岐するため、このソリューションよりも時間がかかります。


1

このように (を含むテストファイルpattern):

shopt -s nullglob
compgen -W *pattern* &>/dev/null
case $? in
    0) echo "only one file match" ;;
    1) echo "more than one file match" ;;
    2) echo "no file match" ;;
esac

compgen -Gより多くのケースをより正確に識別できるため、これははるかに優れています。

ワイルドカードを1つだけ使用できます *


0
if ls -d $glob > /dev/null 2>&1; then
  echo Found.
else
  echo Not found.
fi

多くの一致がある場合、またはファイルアクセスが遅い場合、これは非常に時間がかかる可能性があることに注意してください。


1
[a]ファイル[a]が存在し、ファイルaが存在しないときにのようなパターンが使用されている場合、これは誤った答えを返します。一致する必要がある唯一のファイルaが実際には存在しない場合でも、「found」と表示されます。
Chris Johnsen、

このバージョンは通常のPOSIX / bin / sh(bashismsなし)で動作するはずです。必要な場合は、グロブに角括弧はありません。そのため、ひどく病理。しかし、グロブに一致するファイルがあるかどうかをテストする良い方法はないと思います。
ケンブルーム

0
#!/bin/bash
set nullglob
touch /tmp/foo1 /tmp/foo2 /tmp/foo3
FOUND=0
for FILE in /tmp/foo*
do
    FOUND=$((${FOUND} + 1))
done
if [ ${FOUND} -gt 0 ]; then
    echo "I found ${FOUND} matches"
else
    echo "No matches found"
fi

2
正確に1つのファイルが一致する場合、このバージョンは失敗しますが、nullglobシェルオプションを使用することにより、FOUND = -1 kludgeを回避できます。
ケンブルーム2010年

@ケン:うーん、私はnullglobクラッジを呼ばないでしょう。単一の結果を元のパターンと比較するのは簡単ではありません(誤った結果が発生する傾向があります)nullglob
Chris Johnsen、

@クリス:あなたは誤解していると思う。私はnullglobクラッジを呼ばなかった。
ケンブルーム2010年

1
@ケン:確かに、私は誤解しました。私の無効な批判に対する私の謝罪を受け入れてください。
Chris Johnsen、

-1
(ls glob* &>/dev/null && echo Files found) || echo No file found

5
一致するディレクトリがglob*あり、たとえばそれらのディレクトリをリストするための書き込みがない場合にも、falseを返します。
ステファンChazelas 2013年

-1

ls | grep -q "glob.*"

最も効率的な解決策ではありません(ディレクトリに大量のファイルがある場合、遅くなる可能性があります)が、シンプルで読みやすく、正規表現がプレーンなbash globパターンよりも強力であるという利点もあります。


-2
[ `ls glob* 2>/dev/null | head -n 1` ] && echo true

1
より良い答えを得るには、コードにいくつかの説明を追加してみてください。
Masoud Rahimi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.