설정되지 않은 변수를 사용하면 해가 되나요?

설정되지 않은 변수를 사용하면 해가 되나요?

다음 코드가 있다고 가정해 보겠습니다.

# Check if the color prompt is enabled and supported on this system
if [ -n "$force_color_prompt" ] && [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
    GREEN="\033[1;32m"
    DIM="\033[2m"
    RESET="\033[00m"
fi

echo -e "Oh, ${GREEN}green${RESET} world, ${DIM}don't desert me now...${RESET}"

색상 지원이 활성화되면 예쁜 색상의 선이 출력됩니다. 색상 지원이 활성화되지 않으면 와 같은 값이 ${GREEN}설정되지 않으며 텍스트는 검정색 배경에 일반적인 흰색으로 인쇄됩니다.

코드는 설정되지 않은 변수가 단순히 빈 문자열로 평가된다는 사실에 의존합니다(내 테스트에서는 그렇습니다). 이로 인해 일부 시스템에서 버그나 문제가 발생합니까, 아니면 존재하지 않는 모든 변수가 발생합니까?언제나빈 문자열로 평가됩니까? 제가 이 정비사에게 의존하면 안되는 이유가 있나요?

답변1

$FOO존재하지 않는 변수는 or (동등하게) 로 확장될 때 항상 빈 문자열로 평가되며 ${FOO}, 특정 경우를 제외하고 이에 따라 아무런 해가 없습니다.

set -u해당 변수를 사용하기 전에 누군가 현재 셸에서 호출한 경우 다음 설정이 활성화된 것입니다.

              -u 매개변수 수행 시 설정되지 않은 변수를 오류로 처리합니다.
                      영원한 확장. 설정되지 않은 항목에서 확장을 시도하는 경우
                      변수에 따라 쉘은 오류 메시지를 인쇄하고, 그렇지 않은 경우
                      대화형, 0이 아닌 상태로 종료됩니다.

즉, 다른 사람이 제어하는 ​​스크립트에 소스를 제공하도록 설계된 함수를 작성하는 경우 설정되지 않은 변수를 사용하는 것에 대해 편집증적이어야 할 수도 있습니다. 그렇지 않으면 해당 변수가 set -u함수를 호출하기 전에 사용한 경우 해당 스크립트 설정되지 않은 변수를 처음 확장하려고 하면 오류 메시지와 함께 종료됩니다.

자신만의 스크립트를 작성하는 경우 빈 문자열로 확장되는 설정되지 않은 변수를 계산해도 아무런 해가 없습니다.

편집하다- 또한 생각해 보세요. 터미널에서 terminfo 색상 기능을 사용할 수 있는지 여부에 따라 모든 것을 조건부로 만들고 있으므로 vt100 값을 하드코딩하는 대신 실제로 terminfo를 사용하여 시퀀스를 생성하는 것은 어떨까요? 다음과 같은 것 :

if [ -n "$force_color_prompt" ] && type tput &>/dev/null; then
    GREEN="$(tput setaf 2)$(tput bold)"
    DIM="$(tput dim)"
    RESET="$(tput sgr0)"
fi

이렇게 하면 다른 터미널에서 어느 정도 이식성을 얻을 수 있습니다(물론 표시된 코드를 사용하지 않는 터미널의 수는 적고 줄어들고 있습니다). 또한 terminfo 정의가 얼마나 정확한지에 따라 일부 플랫폼에서는 일부 기능이 존재하지 않을 수 있으므로 일부 이식성을 잃을 수도 있습니다. YMMV.

답변2

POSIX 호환 쉘 스크립팅 언어의 가장 독특한 기능 중 하나는매개변수 확장. 일반적으로 변수 값과 연관되지 않는 작업을 수행하기 위해 다양한 방법으로 사용될 수 있습니다. 셸에서 변수는 단순한 값 이상일 가능성이 있습니다. 실행 가능한 항목이 될 수 있습니다. 자체적으로 테스트할 수 있는 가능성이 있습니다. 그리고 이는 쉘 옵션을 설정할 필요 없이 명시적으로 제공됩니다.

예를 들어 코드는 다음과 같습니다.

N= ERR='error encountered - exiting' 
: ${force_color_prompt?"$ERR"}
/usr/bin/tput setaf >/dev/null 2>&1 || ${N:?"$ERR"}
: "${GREEN:=\033[1;32m}" "${DIM:=\033[2m}" "${RESET:=\033[00m}"
printf %b\\n \
    "Oh, ${GREEN}green${RESET} world, ${DIM}don't desert me now...${RESET}"

변수 $N는 명시적으로 널 문자열로 설정되므로 매개변수 확장 형식으로 평가할 때 ${N:?}상위 쉘이 자동으로 종료되고 다음 명령문도 ?확장에 대해 평가되며 그 결과는 에 출력됩니다 stderr. 마찬가지입니다 $force_color_prompt. 설정되지 않은 경우 스크립트는 오류와 함께 종료되고 출력은 모두 자동 $ERR으로 이루어집니다.stderr

$GREEN $RESET$DIM현재 설정되지 않았거나 null 문자열로 설정된 경우 정의한 값으로 설정됩니다 ''. 이를 통해 해당 값을 환경 변수로 스크립트에 전달할 수 있습니다. 예를 들어 위의 코드 조각이 호출된 스크립트에 있고 greenworld.sh다음과 같이 호출한 경우:

GREEN="$(tput setaf 2)$(tput bold)" greenworld.sh

그러면 $GREEN스크립트 내용이 재설정되지 않고 대신 내가 설정한 명시적 값이 상속됩니다. 이는 쉘 스크립트를 유연하게 만듭니다.

그리고 tputGodlygeek이 권장한 대로 그런 방식으로 사용하는 것은 우선적으로 권장하는 것입니다.

셸에서 설정되지 않은 변수는 때때로 설정된 변수만큼 유용할 수 있습니다. 다음은 다른 예입니다.

set -- * 
while ${1+:} false ; do
    #do stuff until all positionals are shifted away
shift ; done

이 예에서는 첫 번째 매개변수가 정의되어 있는 한 쉘의 내장 :null로 확장되어 결과적으로 다음 false호출이 작동하지 않게 됩니다. 그러나 모든 위치 매개변수가 사라지자마자 shift그런 ${1}식으로 확장되지 않고 false호출되며 while루프가 종료됩니다. 이것에 대해 수많은 변형을 할 수 있습니다.

답변3

일반적인 사용에서, 즉 단지 변수를 확장하는 경우, 설정되지 않은 변수는 모든 Bourne/POSIX 스타일 쉘에서 빈 것으로 처리됩니다. 예외는 set -o unsetaka가 set -u적용되는 경우입니다. 이 경우 설정되지 않은 변수의 값에 액세스하려고 하면 쉘에서 오류가 발생합니다(참조godlygeek의 답변).

변수가 설정되지 않았거나 비어 있는지 테스트하는 방법이 있습니다. 예를 들어, 구성은 설정된 경우(비어 있더라도) 값과 설정되지 않은 경우 ${foo-bar}의 값으로 확장됩니다 . (콜론 추가) 빈 변수를 설정되지 않은 것처럼 처리합니다. 다른 유사한 확장 구성인 , 는 유사하게 작동합니다. 다른 차이점 중에서 및 (내보낸 경우) 의 출력에도 빈 변수가 나타납니다 . 원하는 경우에만 이러한 차이점을 접하게 됩니다.foobarfoo
${foo:-bar}${foo+bar}${foo?bar}${foo=bar}setexport

그러나, 다른 이유가 있습니다항상 변수를 초기화: 변수가 실제로 설정되지 않았는지 어떻게 알 수 있나요? 호출자가 자신의 목적을 위해 유사한 변수를 정의했을 수도 있습니다. 호출자가 동일한 스크립트의 다른 부분인 경우 변수를 로컬(ksh/bash/zsh에서만 가능)로 선언하지 않는 한 함수에서의 사용은 호출자의 것을 덮어쓰게 되므로 변수 이름 충돌이 문제가 됩니다. 그래도. 그러나 다른 사람이 귀하와 동일한 변수 이름을 선택했기 때문에 해당 변수가 환경에 존재할 수도 있습니다. 쉘 변수에 소문자 이름을 사용하고 환경 변수에 대문자 이름을 사용하는 일종의 관례가 있지만 모든 충돌을 해결하지는 못하고 보편적으로 따르지 않습니다.

$ 내보내기 DIM=1 SUM=42
$ 배쉬
오, 푸른 세상이여, 이제 나를 버리지 마세요...

관련 정보