ファイル名が中括弧で拡張されたglobパターンセットの1つと一致するかどうかをプログラムでどのように確認できますか?

ファイル名が中括弧で拡張されたglobパターンセットの1つと一致するかどうかをプログラムでどのように確認できますか?

$string文字列がグローバルパターンと一致するかどうかを知りたいです$pattern$string既存のファイルの名前であってもなくてもよい。どうすればいいですか?

私の入力文字列が次の形式であるとしましょう。

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

$stringbashイディオムが一致するか、他の任意のグローバルパターンと一致するかどうかを確認します。これまでに試したことは次のとおりです。$pattern1$pattern2

  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グローバルまたはワイルドカードパターンではなく拡張正規表現として解釈されるため、これは機能しません。

答え1

この問題に対する普遍的な解決策はありません。その理由は、bashで中括弧拡張(別名{pattern1,pattern2,...}およびファイル名拡張(別名globパターン))が別々に扱われ、異なる条件と時間に拡張されるためです。以下は、bashで実行される拡張の完全なリストです。

  • 支柱の拡張
  • チルダ拡張
  • パラメータと変数の拡張
  • コマンドの置き換え
  • 算術拡張
  • 噴射
  • パス名拡張

これらのサブセット(おそらく中括弧、チルダ、パス名の拡張)にのみ興味があるため、特定のパターンとメカニズムを使用して制御可能な方法で拡張を制限できます。たとえば、

#!/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 ]]かっこ拡張を実行した後、テスト操作を使用してパス名拡張をテストできます。

答え2

私は信じていません{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

答え3

Patrickが指摘したように、「異なる種類」のスキーマが必要です。

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


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

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

答え4

$string保存されたglob拡張によるファイルパスにあることを確認する場合$pattern(他の人が言ったように、{foo,bar}これはglob演算子ではないことを覚えておいてください)zsh

if ()(($#)) $~pattern(NY1e['[[ $REPLY = $string ]]']); then
  print -r -- "$string is among the result of the $pattern glob expansion"
fi

を使用すると、bashいつでもループを使用できます。

among() (
  string=$1 pattern=$2 IFS=
  shopt -s nullglob extglob
  for file in $pattern@(); do
    [[ "$string" = "$file" ]] && return
  done
  false
)
if among "$string" "$pattern"; then
  printf '%s\n' "$string is among the result of the $pattern glob expansion"
fi

extglob有効にすると、次のようなものを使用できるksh拡散演算子のサブセットを有効にできます。foo/@(foo|bar)上記@()のパターンに加えてグローバル拡散を強制します。これがなければ、存在しなくてもamong foo footrueが返されますfoo。 donを使用すると、/最後にパターンが適用されません(例among /bin/ '/*/':)。

スプリット+グロブに加えて、中かっこの拡張は、有効でなく無効になっていない限り、引数のksh拡張中に行われます。 / に対応するglob演算子もあるので、次のようにできます。noglobbraceexpandksh93~(N)zshbashnullglob

function among {
  typeset string="$1" pattern="$2" IFS= file
  set -o braceexpand +o noglob
  for file in ~(N)$pattern; do
    [[ "$string" = "$file" ]] && return
  done
  false
}

among foo/bar 'foo/{bar,baz}'ファイルが存在する限りtrueを返しますfoo/bar

kshの拡張glob演算子は、他の拡張の結果では認識されません(a='@(x)'; echo $a出力のPOSIX要件に準拠するため)。@(x)

これらすべてまたは文字列がパターンと一致していてもfalseを返しamong .foo '*'ますamong /etc/issue '*'among /usr/local/bin '/*/bin'

関連情報