ファイル名がシェルグロブパターンに一致するかどうかをプログラムで確認するにはどうすればよいですか?


13

文字列$stringがglobパターンと一致するかどうかを確認したいと思います$pattern$string既存のファイルの名前である場合とそうでない場合があります。これどうやってするの?

入力文字列について次の形式を想定します。

string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

私はかどうかを判断し、bashのイディオム検索したい$stringにマッチしたことになる$pattern1$pattern2または他の任意のglobパターンを。ここに私がこれまで試したものがあります:

  1. [[ "$string" = $pattern ]]

    これはほとんど機能$patternしますが、グロブパターンではなく文字列パターンとして解釈されます。

  2. [ "$string" = $pattern ]

    このアプローチの問題は、$pattern展開されてから文字列比較が実行され$string、の展開であるということ$patternです。

  3. [[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]

    これ$stringは機能しますが、存在するファイルが含まれている場合のみです。

  4. [[ $string =~ $pattern ]]

    =~演算子が$patternグロブまたはワイルドカードパターンではなく、拡張正規表現として解釈されるため、これは機能しません。


2
あなたが遭遇する問題{bar,baz}は、パターンではないということです。パラメーターの拡張です。その中に微妙だが、重要な違いは、{bar,baz}複数の引数に非常に早い段階で拡大し、されるbarbaz
パトリック14年

シェルがパラメーターを展開できる場合、文字列がグロブの潜在的な展開であるかどうかを確認できます。
jayhendren 14年

この=を試してみるls /foo/* あなたが一致することができ、今
Hackaholic

1
@Patrick:bashのマニュアルページを読んだ後、foo/{bar,baz}実際には(パラメータ展開ではなく)ブレース展開であり、foo/*パス名展開であることがわかりました。 $stringパラメータ展開です。これらはすべて、異なる時間に、異なるメカニズムで行われます。
jayhendren 14年

@jayhendren @Patrickは正しいです、そしてあなたはあなたの質問が最終的にタイトルが信じさせるものではないことを学びました。むしろ、文字列をさまざまな種類のパターンと照合する必要があります。globパターンと厳密に一致させたい場合、このcaseステートメントはBashマニュアルに従ってPathname Expansion( "globbing")を実行します。
マイクS

回答:


8

この問題に対する一般的な解決策はありません。その理由は、bashでは、ブレースの展開(つまり、{pattern1,pattern2,...}ファイル名の展開(グロブパターンとも呼ばれる)は別のものと見なされ、さまざまな条件下でさまざまな時点で展開されるからです。

  • ブレースの拡張
  • チルダ展開
  • パラメータと変数の展開
  • コマンド置換
  • 算術展開
  • 単語分割
  • パス名展開

これらのサブセット(ブレース、チルダ、パス名の展開など)のみに関心があるため、特定のパターンとメカニズムを使用して、制御可能な方法で展開を制限することができます。例えば:

#!/bin/bash
set -f

string=/foo/bar

for pattern in /foo/{*,foo*,bar*,**,**/*}; do
    [[ $string == $pattern ]] && echo "$pattern matches $string"
done

このスクリプトを実行すると、次の出力が生成されます。

/foo/* matches /foo/bar
/foo/bar* matches /foo/bar
/foo/** matches /foo/bar

これが機能set -fするのは、パス名の展開が無効になっているためfor pattern in /foo/{*,foo*,bar*,**,**/*}です。そのため、ステートメントではブレース展開とチルダ展開のみが発生します。その後、テスト操作[[ $string == $pattern ]]を使用して、ブレースの展開が既に実行された後にパス名の展開をテストでき ます。


7

私はそれ{bar,baz} シェルグロブパターンであるとは思いません(確かに/foo/ba[rz]そうですが)が、$stringマッチするかどうか知りたい場合は、$pattern次のことができます:

case "$string" in 
($pattern) put your successful execution statement here;;
(*)        this is where your failure case should be   ;;
esac

あなたが好きなだけ行うことができます:

case "$string" in
($pattern1) do something;;
($pattern2) do differently;;
(*)         still no match;;
esac

1
こんにちは@mikeserv、私が上記で提供したコメントと答えに示されているように、あなたの言うことは真実であるということをすでに知っています{bar,baz}-globパターンではありません。私はすでにこれを考慮に入れた私の質問に対する解決策を考え出しています。
jayhendren 14年

3
+1これは、タイトルと最初の文に記載されているとおりに質問に答えます。ただし、この質問は、他のパターンをシェルグロブパターンでまとめます。
マイクS

3

パトリックが指摘したように、「異なるタイプ」のパターンが必要です。

[[ /foo/bar == /foo/@(bar|baz) ]]


string="/foo/bar"
pattern="/foo/@(bar|baz)"
[[ $string == $pattern ]]

引用符は必要ありません。


OK、これは機能しますが、厳密に言えば、私の質問には答えません。たとえば、別のソースからのパターンを検討したいと思います。つまり、パターンが制御不能です。
jayhendren 14年

@jayhendrenその後、おそらく最初に着信パターンをbashが受け入れるものに変換する必要があります。
ホークレイジング14年

だからあなたが本当にやったことは、「ファイル名が式の潜在的な展開であるかどうかをどのように見分けるか」から「通常のbashスタイルのファイル名パターンをbashスタイルの拡張globパターンに変換する方法」から
ジェイヘンドレン

@jayhendrenあなたが望んでいることは不可能だと考えると、「あなたが本当にやったこと」は少し奇妙に聞こえますが、多分それはただの外国語のことです。この新しい質問をしたい場合は、入力パターンがどのように見えるかを説明する必要があります。たぶんそれは簡単なsed操作です。
ホークレイジング14年

1
@HaukeLagingは正しいです。タイトルが「shell glob pattern」と書かれているが、彼の質問の内容は非globパターンを使用しているため、globパターン(別名「Pathname Expansion」)との照合方法を理解するためにここに来る人々は混乱する傾向があります。 。ある時点で、ジェイヘンドレンはその違いを知ったが、彼の最初の混乱がハウケに彼のやり方に答えさせた。
マイクS

1

このようにgrepを使用します。

#!/bin/bash
string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

if echo $string | grep -e "$pattern1" > /dev/null; then
    echo $string matches $pattern1
fi

if echo $string | grep -e "$pattern2" > /dev/null; then
    echo $string matches $pattern2
fi

出力が得られます:

./test2.bsh 
/foo/bar matches /foo/*

-2

zshの場合:

[[ $string = $~pattern ]] && print true

1
質問は、bashシェルが使用されることを示しています。
jayhendren 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.