ステートメントテスト|でaで区切られたさまざまな文字列で構成された変数を使用しようとしています。caseたとえば、

string="\"foo\"|\"bar\""
read choice
case $choice in
    $string)
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac

fooorと入力しbarて、お問い合わせの最初の部分を実行させたいと思いますcase。しかし、両方とも私を二度目にfoo案内します。bar

$ foo.sh
foo
Bad choice!
$ foo.sh
bar
Bad choice!

"$string"代わりに使用しても$string違いはありません。それも動作しませんstring="foo|bar"

私はこれができることを知っています:

case $choice in
    "foo"|"bar")
        echo "You chose $choice";;
    *)
        echo "Bad choice!";;
esac

私はさまざまな解決策を考えることができますが、casebashで変数を条件付きで使用できるかどうか疑問に思います。可能ですか?可能であればどのように達成できますか?

答え1

Bashのマニュアルには次のように記載されています。

[[(]パターン[ |パターン]...)リストの大文字と小文字の単語] ... esac;;

調査された各パターンは、チルダ拡張、パラメータおよび変数拡張、算術置換、コマンド置換、およびプロセス置換を使用して拡張されます。

「パス名拡張」なし

したがって、スキーマは「パス名の拡張」では拡張されません。

したがって、パターン内に「|」を含めることはできません。固有:「|」を使用して2つのパターンをリンクできます。

これは働きます:

s1="foo"; s2="bar"    # or even s1="*foo*"; s2="*bar*"

read choice
case $choice in
    $s1|$s2 )     echo "Two val choice $choice"; ;;  # not "$s1"|"$s2"
    * )           echo "A Bad  choice! $choice"; ;;
esac

「拡張ワイルドカード」を使用してください

ただし、「パス名拡張」ルールを使用して一致するものがありますword。そして「拡張されたワイルドカード」pattern
ここここそして、ここ代替("|")パターンが許可されています。

これはまた働きます:

shopt -s extglob

string='@(foo|bar)'

read choice
    case $choice in
        $string )      printf 'String  choice %-20s' "$choice"; ;;&
        $s1|$s2 )      printf 'Two val choice %-20s' "$choice"; ;;
        *)             printf 'A Bad  choice! %-20s' "$choice"; ;;
    esac
echo

文字列の内容

foo次のテストスクリプトは、2つのうちの1つまたは任意の場所を含むすべての行に一致するパターンが変数とその両方の1つまたは両方であることをbar示しています。'*$(foo|bar)*'$s1=*foo*$s2=*bar*


テストスクリプト:

shopt -s extglob    # comment out this line to test unset extglob.
shopt -p extglob

s1="*foo*"; s2="*bar*"

string="*foo*"
string="*foo*|*bar*"
string='@(*foo*|*bar)'
string='*@(foo|bar)*'
printf "%s\n" "$string"

while IFS= read -r choice; do
    case $choice in
        "$s1"|"$s2" )   printf 'A first choice %-20s' "$choice"; ;;&
        $string )   printf 'String  choice %-20s' "$choice"; ;;&
        $s1|$s2 )   printf 'Two val choice %-20s' "$choice"; ;;
        *)      printf 'A Bad  choice! %-20s' "$choice"; ;;
    esac
    echo
done <<-\_several_strings_
f
b
foo
bar
*foo*
*foo*|*bar*
\"foo\"
"foo"
afooline
onebarvalue
now foo with spaces
_several_strings_

答え2

次のextglobオプションを使用できます。

shopt -s extglob
string='@(foo|bar)'

答え3

caseあるいは、|パイプはすでに解析されているので、両方の変数が必要です。今後スキーマが拡張されます。

v1=foo v2=bar

case foo in ("$v1"|"$v2") echo foo; esac

foo

変数のシェルモードは、引用符の有無にかかわらず、異なる方法で処理されます。

q=?

case a in
("$q") echo question mark;;
($q)   echo not a question mark
esac

not a question mark

答え4

AWKの構文を使用できます

if($field ~ /regex/)

また

if($i ~ var)

変数を入力と比較します(varパラメータとstarパラメータのリスト$*)。

parse_arg_exists() {
  [ $# -eq 1 ] && return
  [ $# -lt 2 ] && printf "%s\n" "Usage: ${FUNCNAME[*]} <match_case> list-or-\$*" \
  "Prints the argument index that's matched in the regex-case (~ patn|patn2)" && exit 1
  export arg_case=$1
  shift
  echo "$@" | awk 'BEGIN{FS=" "; ORS=" "; split(ENVIRON["arg_case"], a, "|")} {
    n=-1
    for(i in a) {
     for(f=1; f<=NF; f++) {
      if($f ~ a[i]) n=f
     }
    }
  }
  END {
    if(n >= 0) print "arg index " n "\n"
    }'
 unset arg_case
}
string="--dot|-d"
printf "testing %s\n" "$string"
args="--dot -b -c"; printf "%s\n" "$args"
parse_arg_exists "$string" "$args"
args="-b -o"; printf "%s\n" "$args"
parse_arg_exists "$string" "$args"
args="-b -d -a"; printf "%s\n" "$args"
parse_arg_exists "$string" "$args"

印刷する:

testing --dot|-d
--dot -b -c
arg index 1
 -b -o
-b -d -a
arg index 2

関連情報