쉘 스크립트는 구문 오류에도 불구하고 0exit_status를 반환합니다.

쉘 스크립트는 구문 오류에도 불구하고 0exit_status를 반환합니다.

다음 스크립트를 고려해보세요.

#!/bin/sh

foo=1
if [[ ! -z $foo ]]; then
    echo abc
fi

Bash 구문 [[ ... ]]을 사용하며 Ubuntu에서 기본 셸(대시)을 사용하여 실행하면 예상대로 작동하지 않습니다. 그러나 반환 코드는 여전히 0입니다.

$ ./tmp.sh
./tmp.sh: 4: ./tmp.sh: [[: not found
$ echo $?
0

종료 코드를 사용할 수 없는 경우 스크립트에서 이러한 오류를 어떻게 감지할 수 있습니까?

답변1

먼저 왜 이런 일이 발생하는지 설명하겠습니다.POSIX 쉘 명령 언어 사양 설명하다:

if 명령의 종료 상태는 실행된 then 또는 else 복합 목록의 종료 상태여야 하며, 실행되지 않은 경우 0이어야 합니다.

귀하의 경우 해당 부분이 실행되지 않고 종료 상태 0이 then없기 때문입니다. elseBash를 사용하여 이 스크립트를 실행하면 다음과 같이 0이 됩니다 man bash.

   if list; then list; [ elif list; then list; ] ... [ else list; ] fi

          The if list is executed.  If its exit status is zero,
          the then list is executed.  Otherwise, each elif list is
          executed in turn, and if its exit status is zero, the
          corresponding then list is executed and the command
          completes.  Otherwise, the else list is executed, if
          present.  The exit status is the exit sta‐ tus of the
          last command executed, or zero if no condition tested
          true.

종료 코드를 사용할 수 없는 경우 스크립트에서 이러한 오류를 어떻게 감지할 수 있습니까?

내가 생각할 수 있는 두 가지 방법이 있습니다.

  • 스크립트를 수정할 수 있으면 elseif 구문에 일부를 추가하세요.

      #!/bin/sh
    
      foo=1
      if [[ ! -z $foo ]]; then
          echo abc
      else
          echo not true
          exit 1
      fi
    
  • 누군가로부터 if를 받았지만 수정하고 싶지 않은 경우 sh패턴에서 shellcheck 정적 분석기를 사용하여 코드에서 가능한 버그를 찾아 작성자에게 보고하십시오.

      $ shellcheck -s sh dash-exit-status.sh
    
      In dash-exit-status.sh line 4:
      if [[ ! -z $foo ]]; then
         ^-------------^ SC2039: In POSIX sh, [[ ]] is undefined.
            ^-- SC2236: Use -n instead of ! -z.
    
      For more information:
        https://www.shellcheck.net/wiki/SC2039 -- In POSIX sh, [[ ]] is undefined.
        https://www.shellcheck.net/wiki/SC2236 -- Use -n instead of ! -z.
    

기본적으로 이것은허점나에게는 실행되어야 하는 스크립트에서 POSIX가 아닌 함수를 사용해서는 안 되며, 이러한 함수는 /bin/sh Bash에 대한 심볼릭 링크일 수도 있지만 반드시 그런 것은 아닙니다.

답변2

@muru가 언급했듯이 이것은 Dash에 관한 한 구문 오류가 아닙니다. are 와 마찬가지로 은 [쉘의 특수 문자가 아닙니다. 예를 들어, 재미있는 이름을 가진 일반적인 명령입니다. 유사한 키워드가 Bash/Ksh/Zsh에서 사용되며 키워드는 셸 구문의 일부이지만 결정적으로 셸에서;([echo[[if아니요이를 지원하면 [[다른 명령으로 처리됩니다.

[[Dash는 이를 지원하지 않으므로 PATH조회가 실패합니다. ~처럼Arkadiusz Drabczyk의 답변여기서 결과는 종료 상태 0입니다. 이것이 실제 구문 오류인 경우 쉘은 0이 아닌 상태로 즉시 종료됩니다.

종료 코드를 사용할 수 없는 경우 스크립트에서 이러한 오류를 어떻게 감지할 수 있습니까?

글쎄요, 기술적으로는...

#!/bin/dash
[[ $1 == "foo" ]]
case $? in
    0)    echo "the first argument was 'foo'";;
    1)    echo "the first argument was something else";;
    127)  echo "[[ not supported";;
    *)    echo "there was some other error";;
esac

또는 동일 ret=$?하고 일련의 if-elifwith 조건이 이에 반대합니다 $ret. 이것은 약간 어색하고 최선의 해결책은 아니지만 특정 명령에 대해 서로 다른 오류 코드를 구별해야 한다면 그렇게 해야 합니다.

목표가 Dash 대 Bash를 감지하는 것이라면 스크립트 시작 부분에 추가 테스트를 추가하여 작동하는지 확인할 수 있습니다 [[.

#!/bin/bash
if ! [[ a == a ]] 2>/dev/null; then
    echo "[[ not supported, exiting." >&2
    exit 1
fi

# ...

마찬가지로, 사용하는 다른 모든 비 POSIX 기능에 대한 테스트를 추가할 수 있지만 테스트해 보세요.모두물론 조금 어색할 수도 있습니다.

동일한 기능을 가진 쉘(Ksh 및 Zsh는 일부와 호환되지만 전부는 아님) 대신 Bash를 정확하게 요청하려면 $BASH_VERSION스크립트 시작 부분에서 다음을 확인하십시오.

#!/bin/bash
if [ -z "$BASH_VERSION" ]; then
    echo "Running only with Bash supported, exiting" >&2
    exit 1
fi

# ...

(물론 변수를 수동으로 설정하는 것도 가능하지만 사용자가 무엇을 하고 있는지 아는 것이 가장 좋습니다.)

관련 정보