다른 변수에 따라 변수에 다른 값을 간결하게 할당하려면 어떻게해야합니까?


20

이 쉘 스크립트를 어떻게 단축 할 수 있습니까?

CODE="A"

if test "$CODE" = "A"
then
 PN="com.tencent.ig"
elif test "$CODE" = "a"
 then
 PN="com.tencent.ig"
elif test "$CODE" = "B"
 then
 PN="com.vng.pubgmobile"
elif test "$CODE" = "b"
 then
 PN="com.vng.pubgmobile"
elif test "$CODE" = "C"
 then
 PN="com.pubg.krmobile"
elif test "$CODE" = "c"
 then
 PN="com.pubg.krmobile"
elif test "$CODE" = "D"
 then
 PN="com.rekoo.pubgm"
elif test "$CODE" = "d"
 then
 PN="com.rekoo.pubgm"
else
 echo -e "\a\t ERROR!"
 echo -e "\a\t CODE KOSONG"
 echo -e "\a\t MELAKUKAN EXIT OTOMATIS"
 exit
fi

2
이것이 bash코드 라고 생각 합니까? 아니면 다른 껍질을 염두에두고 있습니까?
프레디

3
앞으로도 URL 등의 개인 정보를 "com.hello.world"와 같은 일반적인 정보로 바꾸는 것이 좋습니다.
Trevor Boyd Smith

1
@IISomeOneII 대신 CodeGolf.SE를 요청해야합니다. : P
mackycheese21

3
@Trevor, 내가 권하고 싶습니다 example.org, example.net이 도메인은 특히 RFC 2606에서이 목적을 위해 예약되어 실제 개체에 사용되지 않습니다로서, 등.
Toby Speight

2
"hello.com"은 Google이 소유하고 있기 때문에 @TrevorBoydSmith Seconding Toby의 com.example 등 권장 사항.
David Conrad

답변:


61

case명령문을 사용하십시오 (이동식, 다른 sh쉘 에서 작동 ).

case "$CODE" in
    [aA] ) PN="com.tencent.ig" ;;
    [bB] ) PN="com.vng.pubgmobile" ;;
    [cC] ) PN="com.pubg.krmobile" ;;
    [dD] ) PN="com.rekoo.pubgm" ;;
    * ) printf '\a\t%s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS' >&2
        exit 1 ;;
esac

변수 이름을 모두 대문자 (예 CODE:)에서 소문자 code또는 대소 문자 (예 : 또는 Code)로 변경하는 것이 좋습니다 . 특별한 의미를 가진 모든 대문자 이름이 있으며 실수로 그 중 하나를 재사용하면 문제가 발생할 수 있습니다.

기타 참고 사항 : 표준 규칙은 오류 메시지를 "표준 출력"이 아닌 "표준 오류"로 보내는 것입니다. >&2리디렉션이 작업을 수행합니다. 또한 스크립트 (또는 프로그램)가 실패하면 0이 아닌 상태 ( exit 1) 로 종료하는 것이 가장 좋으므로 호출 컨텍스트는 무엇이 잘못되었는지 알 수 있습니다. 다른 상태를 사용하여 다른 문제를 나타낼 수도 있습니다 ( 좋은 예 curl매뉴얼 페이지 의 "EXIT CODES"섹션 참조 ). (여기에 대한 제안은 Stéphane Chazelas와 Monty Harder에게 감사드립니다.)

OS, 버전, 설정 등 사이에서 이식성이 뛰어 나기 때문에 (및 ) printf대신을 추천 합니다 . OS 업데이트에 다른 옵션으로 컴파일 된 bash 버전이 포함되어있어 동작 방식이 변경되어 스크립트가 중단되었습니다 .echo -eecho -necho

여기에 큰 따옴표 $CODE는 필요하지 않습니다. 의 문자열은 case안전하지 않은 몇 가지 컨텍스트 중 하나입니다. 그러나 안전하지 않은 곳과 그렇지 않은 곳을 추적하기가 어렵 기 때문에 변수 참조를 큰 따옴표로 묶는 것을 선호합니다. 따라서 습관적으로 큰 따옴표로 묶는 것이 더 안전합니다.


5
@IISomeOneII 카운트로 계산되고 *오류를 인쇄합니다. 패턴 [aA]은 "a"또는 "A"와 일치하지만 한 번에 모두 일치하지는 않습니다.
고든 데이비스 슨

6
이것은 출력을 stderr로 리디렉션하고 0이 아닌 종료 값을 생성하는 마지막에 와일드 카드 바로 아래에있는 올바른 방법입니다. 변경해야 할 유일한 것은 종료 값이 두 개 이상이므로 반환 값입니다. 더 큰 스크립트에는 exit vales를 정의 readonly Exit_BadCode=1하여 exit $Exit_BadCode대신 말할 수있는 섹션 (아마도 다른 파일에서 제공) 이있을 수 있습니다 .
Monty Harder

2
최근 떠들썩한 파티에가는 경우, 사용 case "${CODE,}" in그래서 조건문의 각 단지가되도록, a), b)
스티브

2
@MontyHarder 그것은 다릅니다. 각각이 문자열에 해당하는 수백 개의 코드가 있다면 다른 접근법이 더 나을 수 있습니다. 당면한 문제는 이것으로 충분합니다.
Kusalananda

2
@MontyHarder 죄송합니다. 더 명확 해졌습니다. "코드"는 의미 $CODE합니다. 나는 항상 "종료 상태"라고 정확히 부르지 만 결코 "코드"가 아닙니다. 스크립트가 문자열을 참조하기 위해 수백 개의 를 사용해야 하는 경우 case명령문을 사용하면 다루기가 어려워집니다.
Kusalananda

19

bash릴리스 4.0 이상을 사용한다고 가정하면 ...

CODE=A

declare -A domain

domain=(
   [a]=com.tencent.ig
   [b]=com.vng.pubgmobile
   [c]=com.pubg.krmobile
   [d]=com.rekoo.pubgm
)

PN=${domain[${CODE,,}]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

이 코드에서는 모든 단일 도메인 이름을 포함하는 연관 배열을 정의합니다. 각 도메인 이름은 각각 단일 문자 소문자 키와 연관되어 있습니다.

$PN변수는 낮은 맡았다에 해당하는 도메인 이름 할당 $CODE(값 ${CODE,,}반환에게 값 $CODE만 소문자로 전환)이 배열을하지만,이 경우 $CODE에 유효한 항목에 해당하지 않는 domain목록, 그것은으로 스크립트를 종료 오류.

${variable:?error message}매개 변수 대체 값으로 확장 할 것입니다 $variable(코드에서 해당 도메인)하지만 값을 사용할 수 비어 있지 않으면 오류 메시지와 함께 스크립트를 종료합니다. 코드에서와 정확히 같은 형식의 오류 메시지를 얻지는 않지만 유효하지 않은 경우 본질적으로 동일 하게 작동 합니다 $CODE.

$ bash script.sh
script.sh: line 12: domain[${CODE,,}]: ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS

당신이 문자 수에 관심이 있다면, 우리는 이것을 더 짧게 할 수 있습니다 :

CODE=A
declare -A domain=( [a]=tencent.ig [b]=vng.pubgmobile [c]=pubg.krmobile [d]=rekoo.pubgm )
PN=com.${domain[${CODE,,}]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

불필요한 줄 바꿈을 삭제하는 것 외에도 com.각 도메인에서 제거 했습니다 (대신에 할당에 추가됨 PN).

위의 모든 코드는 다중 문자 값의 $CODE경우 에도 작동 합니다 ( domain배열 에 소문자 키가 존재하는 경우).


경우 $CODE(제로) 인덱스 대신 숫자이고,이 코드를 조금 단순화하는 것입니다 :

CODE=0

domain=( com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm )
PN=${domain[CODE]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

또한 domain한 줄에 하나의 항목을 포함하는 보조 파일에서 배열을 쉽게 읽을 수 있습니다.

CODE=0

readarray -t domain <domains.txt
PN=${domain[CODE]:?ERROR! CODE KOSONG, MELAKUKAN EXIT OTOMATIS}

1
@IISomeOneII는 declare -A domain단지 domain연관 배열 ( "해시") 변수 여야 한다고 말합니다 .
Kusalananda

1
@Isaac 이제 당신과 더 구별됩니다. 고마워요
Kusalananda

1
zsh 또는 ksh93을 사용하는 것이 좋습니다. bash의 경우 최신 버전이 필요하며 빈 값이 인 경우 실패합니다 $CODE.
스테판 Chazelas

1
@ StéphaneChazelas 예, $CODE설정되지 않았거나 비어있는 경우 잘못된 배열 첨자에 대한 추가 오류 메시지가 표시 되지만 그 후에도 여전히 올바른 사용자 지정 오류 메시지가 생성됩니다.
Kusalananda

1
@Kusalananda 새로운 (유효한 POSIX) 스크립트가 게시되었습니다. 오류 검사가 없으면 매우 짧습니다.
이삭

11

쉘이 배열을 허용한다면 가장 짧은 대답은 bash의 다음 예와 같아야합니다.

declare -A site
site=( [a]=com.tencent.ig [b]=com.vng.pubgmobile [c]=com.pubg.krmobile [d]=com.rekoo.pubgm )

pn=${site[${code,}]}

그것은 $codea, b, c 또는 d 만 될 수 있다고 가정합니다 .
그렇지 않은 경우 다음과 같은 테스트를 추가하십시오.

case ${site,} in
    a|b|c|d)        pn=${site[${code,}]};;
    *)              pn="default site"
                    printf '\a\t %s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS'
                    exit 1
                    ;;
esac

입력이 A이면 해당 스크립트에서 작동합니까? 내 영어 나쁜 죄송합니다
IISomeOneII

2
예, 확장 ${var,}은의 첫 문자를 소문자로 변환합니다 ${var}. @IISomeOneII
아이작

1
${var,}그래도 Bash 전용 인 것 같습니다. 내가 연관 배열이 KSH에서 일하는 것 같아 너무 zsh을
ilkkachu

@ilkkachu 네, 두 가지 방법으로 모두 정정하십시오.
이삭

모두들, 여기 좋은 사람들이 많이 있습니다 👍
IISomeOneII

3

이 답변을 다른 방향으로 가져 가겠습니다. 데이터를 스크립트로 코딩하는 대신 해당 데이터를 별도의 데이터 파일에 넣은 다음 코드를 사용하여 파일을 검색하십시오.

$ cat names.cfg 
a com.tencent.ig
b com.vng.pubgmobile
c com.pubg.krmobile
d com.rekoo.pubgm

$ cat lookup.sh
PN=$(awk -v code="${1:-}" 'tolower($1) == tolower(code) { print $2; }' names.cfg)
if [ -z "${PN}" ]; then
  printf '\a\t%s\n' 'ERROR!' 'CODE KOSONG' 'MELAKUKAN EXIT OTOMATIS' >&2
  exit 1
fi
echo "${PN}"

$ bash lookup.sh A
com.tencent.ig
$ bash lookup.sh a
com.tencent.ig
$ bash lookup.sh x
    ERROR!
    CODE KOSONG
    MELAKUKAN EXIT OTOMATIS

이러한 우려를 분리하면 몇 가지 이점이 있습니다.

  • 코드 논리를 해결하지 않고도 쉽고 간단하게 데이터를 추가 및 제거 할 수 있습니다.
  • 특정 하위 도메인에있는 일치 항목 수를 계산하는 등 다른 프로그램에서 데이터를 재사용 할 수 있습니다.
  • 당신이있는 경우 데이터의 목록을, 당신은 디스크 사용에 정렬 할 수 있습니다 look효율적 이진 검색이 (가 아닌 라인 라인에 의해 grep또는 awk)

1
이 방법으로 이동하면 여전히 PN올바른 값으로 설정해야합니다.
카추

1
@ilkkachu 페어 포인트. 나는 OP에서 그것을 놓쳤다. 수정했습니다.
주교

2
코드에서 데이터를 분리하기위한 +1
arp

1

문자를 사용하여 값을 인덱싱하고 숫자를 사용하는 경우 다음과 같이 간단 해집니다.

code=1
set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm

eval pn\=\${"$code"}

그것은 휴대용 쉘 코드이며 대부분의 쉘에서 작동합니다.
bash의 pn=${!code}경우을 사용하거나 bash / ksh / zsh의 경우을 사용할 수 있습니다 pn=${@:code:1}.

편지

사용자 문자 (a에서 z로 또는 A에서 Z로)를 색인으로 변환해야하는 경우 :

code=a                              # or A, B, C, ... etc.
set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm
eval pn\=\"\${$(( ($(printf '%d' "'$code")|32)-96  ))}\"

각 부분의 의도와 의미를 명확히하기 위해 더 긴 코드에서 :

code=A

set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm

asciival=$(( $(printf '%d' "'$code") ))      # byte value of the ASCII letter.
upperval=$(( asciival |  32 ))               # shift to uppercase.
indexval=$(( upperval -  96 ))               # convert to an index from a=1.
eval arg\=\"\$\{$indexval\}\"                # the argument at such index.

소문자 값으로 변환해야하는 경우 다음을 사용하십시오 $(( asciival & ~32 ))(아스키 값의 비트 6이 설정되지 않았는지 확인).

에러 코드

스크립트가 오류에 대해 출력하는 출력은 매우 길고 특히 깁니다.
이를 처리하는 가장 다양한 방법은 함수를 정의하는 것입니다.

errorcode(){ exitcode=$1; shift; printf '\a\t %s\n' "$@"; exit "$exitcode"; }

그런 다음 필요한 특정 메시지를 사용하여 해당 함수를 호출하십시오.

errorcode 27  "ERROR!" "CODE KOSONG" "MELAKUKAN EXIT OTOMATIS"

결과 종료 값은 다음과 같습니다 exitcode(예 : 27).

전체 스크립트 (오류 검사 포함)는 다음과 같습니다.

errorcode(){ exitcode=$1; shift; printf '\a\t %s\n' "$@"; exit "$exitcode"; }

code=${1:-A}

case "$code" in 
    [a-d]|[A-D]) : ;;
    *)           errorcode 27  "ERROR!" "CODE KOSONG" "MELAKUKAN EXIT OTOMATIS" ;;
esac

set -- com.tencent.ig com.vng.pubgmobile com.pubg.krmobile com.rekoo.pubgm
eval pn\=\"\${$(( ($(printf '%d' "'$code") & ~32) - 64  ))}\"

printf 'Code=%s Argument=%s\n' "$code" "$pn"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.