문 테스트 |로 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;;

조사된 각 패턴은 물결표 확장, 매개변수 및 변수 확장, 산술 대체, 명령 대체 및 프로세스 대체를 사용하여 확장됩니다.

"경로 이름 확장" 없음

따라서 스키마는 "경로 이름 확장"을 통해 확장되지 않습니다.

따라서 패턴 안에는 "|"가 포함될 수 없습니다. 고유: "|"를 사용하여 두 패턴을 연결할 수 있습니다.

이것은 작동합니다:

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다음 테스트 스크립트는 둘 중 하나 또는 임의의 위치를 ​​포함하는 모든 행과 일치하는 패턴이 변수 및 둘 다 중 하나 또는 둘 다임 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

관련 정보