[unix] 변수를 사례 조건으로 사용하려면 어떻게해야합니까?

명령문 테스트 |로 분리 된 다른 문자열로 구성된 변수를 사용하려고합니다 case. 예를 들면 다음과 같습니다.

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

문의 첫 부분 을 입력 foo하거나 bar실행할 수 있기를 원합니다 case. 그러나 두 foobar두 번째에 데려다 :

$ 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에서 변수를 조건 으로 사용할 수 있는지 알고 싶습니다 . 가능하다면 어떻게합니까?



답변

bash 매뉴얼은 다음과 같이 말합니다.

[[(] pattern [| pattern] …) 목록의 대소 문자 구분; ] … 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

«확장 글 로빙»사용

그러나 «Pathname Expansion»규칙 wordpattern사용 하는 것과 일치 합니다.
그리고«확장 글 로빙» 여기 , 여기 , 그리고 여기 에서 교차 ( “|”) 패턴을 사용할 수 있습니다.

이것은 또한 작동합니다 :

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_


답변

extglob옵션을 사용할 수 있습니다 :

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


답변

패턴이 확장 되기 전에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


답변

대시 호환 해결 방법을 원하면 다음과 같이 작성할 수 있습니다.

  string="foo|bar"
  read choice
  awk 'BEGIN{
   n=split("'$string'",p,"|");
   for(i=1;i<=n;i++)
    if(system("\
      case \"'$choice'\" in "p[i]")\
       echo \"You chose '$choice'\";\
       exit 1;;\
      esac"))
     exit;
   print "Bad choice"
  }'

설명 : awk는 “문자열”을 분할하고 각 부분을 개별적으로 테스트하는 데 사용됩니다. “choice”가 현재 테스트 된 부품 p [i]와 일치하면 awk- 명령은 11 행에서 “exit”로 종료됩니다. 매우 테스트를 위해 쉘의 “case”가 사용됩니다 ( ‘시스템’호출 내). terdon의 요청에 따라 이것은 쉘의 “case”가 허용하는 것처럼 “foooo”( “search pattern”)에 대해서도 일치하도록 의도 된 test- “string”을 “foo * | bar”로 수정하는 가능성을 유지합니다. 대신 정규 표현식을 선호하는 경우 “시스템”호출을 생략하고 awk의 “~”또는 “match”를 대신 사용할 수 있습니다.


답변