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


17

명령문 테스트 |로 분리 된 다른 문자열로 구성된 변수를 사용하려고합니다 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에서 변수를 조건 으로 사용할 수 있는지 알고 싶습니다 . 가능하다면 어떻게합니까?


2
나는 그것을 실제 답변으로 제안 할 는 없지만 다른 사람이 언급하지 않았으므로 eval$ 선택, 이스케이프, 별표, 세미콜론 및 줄 바꿈을 탈출 하여 사례 진술을 래핑 할 있습니다. 추악하지만 "작동"합니다.
Jeff Schaller

@ JeffSchaller-그것은 나쁜 생각이 아니며 많은 경우이 티켓 일뿐입니다. 나는 그것을 추천하는 것을 고려했지만 read비트가 나를 막았다. 내 의견으로는 사용자 입력 유효성 검사는 case패턴으로 평가 목록의 최상위에 있지 않아야하며 *기본 패턴 으로 잘라내어 도달하는 유일한 결과가 허용되도록 보장해야합니다. 여전히 문제는 파싱 / 확장 순서이기 때문에 두 번째 평가가 필요할 수 있습니다.
mikeserv

답변:


13

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_

9

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

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

흥미있는; 무엇에 @(foo|bar)비해 특별 foo|bar합니까? 둘 다 문자 그대로 입력했을 때 동일하게 작동하는 유효한 패턴입니다.
chepner

4
아, 신경 쓰지 마 |의 패턴의 일부가 아니며 foo|bar, case하나의 절에서 여러 패턴을 허용 하는 명령문 구문의 일부입니다 . | 이다 비록 확장 패턴의 일부.
chepner

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

-1

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

  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"를 대신 사용할 수 있습니다.


친애하는 downvoter, 당신이 당신의 downvote의 이유를 말해 주면 쓸모없는 솔루션 후보를 피하는 방법을 더 잘 배울 수 있습니다.
Gerald Schade 5
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.