변수 할당의 반환 상태는 어떻게 결정됩니까?

변수 할당의 반환 상태는 어떻게 결정됩니까?

스크립트에서 다음 구성을 본 적이 있습니다.

if somevar="$(somecommand 2>/dev/null)"; then
...
fi

이것이 어딘가에 문서화되어 있습니까? 변수의 반환 상태는 어떻게 결정되며 명령 대체와 어떤 관련이 있습니까? (예를 들어, 동일한 결과를 얻을 수 있습니까 if echo "$(somecommand 2>/dev/null)"; then?)

답변1

문서화되어 있습니다 (POSIX의 경우)2.9.1 간단한 명령 그룹 기본 사양을 공개합니다. 거기에는 텍스트 벽이 있습니다. 마지막 단락에 주의를 기울이십시오.

명령 이름이 있으면 다음에 설명된 대로 실행을 계속해야 합니다.명령어 검색 및 실행. 명령 이름은 없지만 명령에 명령 대체가 포함된 경우 명령은 마지막으로 실행된 명령 대체의 종료 상태로 완료되어야 합니다. 그렇지 않으면 명령은 0 종료 상태로 완료되어야 합니다.

예를 들어,

   Command                                         Exit Status
$ FOO=BAR                                   0 (but see also the note from icarus, below)
$ FOO=$(bar)                                Exit status from "bar"
$ FOO=$(bar)$(quux)                         Exit status from "quux"
$ FOO=$(bar) baz                            Exit status from "baz"
$ foo $(bar)                                Exit status from "foo"

bash도 이런 식으로 작동합니다. 하지만 마지막에 있는 "그다지 단순하지 않음" 섹션도 참조하세요.

PHK, 그의 질문에할당은 명령 대체가 없는 한 종료 상태가 있는 명령과 같습니다.,제안

...할당 자체가 명령으로 간주되는 것 같습니다...종료 값은 0이지만 할당의 오른쪽 앞에 적용됩니다(예: 명령 대체 호출...)

그것은 문제를 보는 끔찍한 방법이 아닙니다. 간단한 명령( ;, &또는 |제외 ) &&의 반환 상태를 결정하는 대략적인 방식은 다음 ||과 같습니다.

  • 끝이나 명령 단어(보통 프로그램 이름)에 도달할 때까지 왼쪽에서 오른쪽으로 줄을 스캔합니다.

  • 변수 할당이 표시되면 해당 줄의 반환 상태는 아마도 0일 것입니다.

  • 명령 대체가 표시되면 $(…)해당 명령의 종료 상태를 가져옵니다.

  • 실제 명령에 도달하면(명령 대체가 아님) 해당 명령의 종료 상태를 가져옵니다.

  • 행의 반환 상태는 마지막으로 발생한 숫자입니다.
    명령 대체매개변수로와 같은 명령의 경우 foo $(bar)종료 상태를 확인할 수 있습니다 foo. 의역phk 표기법, 여기서의 동작은 다음과 같습니다

      temporary_variable  = EXECUTE( "bar" )
      overall_exit_status = EXECUTE( "foo", temporary_variable )
    

그러나 이것은 좀 너무 단순하다. 전반적인 반품 상태는 다음에서 비롯됩니다.

A=$(명령 1) B=$(명령 2) C=$(명령 3) D=$(명령 4) E=MC 2
종료 상태입니다. 할당 후에 발생하는 할당은 전체 종료 상태를 0으로 설정하지 않습니다.cmd4E=D=

이카루스, 존재하다phk 질문에 대한 그의 대답, 중요한 점을 제시합니다. 변수는 읽기 전용으로 설정할 수 있습니다. 마지막 단락에서 세 번째POSIX 표준 섹션 2.9.1설명하다,

변수 할당이 해당 변수에 값을 할당하려고 시도하는 경우읽기 전용현재 쉘 환경에서 속성이 설정되면(해당 환경에서 할당되었는지 여부에 관계없이) 변수 할당 오류가 발생합니다. 바라보다쉘 오류의 결과이러한 실수의 결과에 대해.

그래서 당신이 말한다면

readonly A
C=Garfield A=Felix T=Tigger

Garfield반환 상태는 1입니다. 문자열 Felix및/또는 Tigger 명령 대체로 대체되는지 여부는 중요하지 않습니다. 하지만 아래 참고 사항을 참조하세요.

2.8.1 쉘 오류의 결과또 다른 텍스트 묶음과 표가 있고 다음으로 끝납니다.

대화형 셸을 종료하지 않아야 하는 표에 표시된 모든 경우에 셸은 오류가 발생한 명령을 더 이상 처리하지 않아야 합니다.

일부 세부 사항은 의미가 있지만 일부는 의미가 없습니다.

  • A=가끔 숙제를 해요중단하다마지막 문장이 지정하는 것처럼 명령줄입니다. 위의 예에서 는 으로 C설정되었지만 설정되지는 않았습니다(물론 둘 다 설정되지 않았습니다  ).GarfieldTA
  • 다시 말하지만, 실행은 되지만 실행은 안 됩니다.C=$(cmd1) A=$(cmd2) T=$(cmd3)cmd1cmd3
    하지만,내 bash 버전(4.1.X 및 4.3.X 포함)에서는하다구현하다. (BTW, 이는 할당의 종료 값이 할당의 오른쪽 앞에 적용된다는 phk의 해석을 더욱 탄핵합니다.)cmd2

하지만 놀라운 점은 다음과 같습니다.

내 bash 버전에서는

읽기 전용 A
C=무엇A=무엇시간 =무엇 명령 0

하다구현하다. 특히,cmd0

C=$(명령 1) A=$(명령 2) 티=$(명령 3)   명령 0
실행하고 실행하지만 실행하지 않습니다. (이것은 명령이 없는 동작과 반대라는 점에 유의하십시오.) 그리고 의 환경에도 설정됩니다. 이것이 bash의 버그인지 궁금합니다.cmd1cmd3cmd2TCcmd0


그렇게 간단하지는 않습니다.

이 답변의 첫 번째 단락은 "간단한 명령"을 나타냅니다.  사양설명하다,

"간단한 명령"은 임의의 순서로 선택적인 변수 할당 및 리디렉션의 시퀀스이며 선택적으로 단어와 리디렉션이 뒤따르고 제어 연산자에 의해 종료됩니다.

이 명령문은 첫 번째 예제 블록의 명령문과 유사합니다.

$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)

처음 3개에는 변수 할당이 포함되고, 마지막 3개에는 명령 대체가 포함됩니다.

그러나 일부 변수 할당은 그렇게 간단하지 않습니다.  큰 타격(1)설명하다,

할당문은 매개변수로 나타날 수도 있습니다.alias,declare,typeset,export,readonly, 그리고local내장 명령(선언주문하다).

을 위한 export,POSIX 사양설명하다,

종료 상태

    0
      모든 이름 피연산자를 성공적으로 내보냈습니다.
    >0
      하나 이상의 이름을 내보낼 수 없거나 -p옵션이 지정되어 오류가 발생했습니다.

POSIX는 지원되지 local않지만큰 타격(1)설명하다,

사용 중 오류가 발생했습니다.local함수 내부가 아닐 때. 반환 상태는 0이 아닌 이상 0입니다.local함수 외부에서 사용됨, 유효하지 않음이름제공하거나이름읽기 전용 변수입니다.

줄 사이를 읽어보면 선언된 명령이 다음과 같은 것을 알 수 있습니다.

export FOO=$(bar)

그리고

local FOO=$(bar)

더 좋아

foo $(bar)

종료 상태를 무시하고 bar 기본 명령( export, local또는 )을 기반으로 foo종료 상태를 제공하는 한 . 그래서 이런 이상한 현상이 나타납니다

   Command                                           Exit Status
$ FOO=$(bar)                                    Exit status from "bar"
                                                  (unless FOO is readonly)
$ export FOO=$(bar)                             0 (unless FOO is readonly,
                                                  or other error from “export”)
$ local FOO=$(bar)                              0 (unless FOO is readonly,
                                                  statement is not in a function,
                                                  or other error from “local”)

우리는 그것을 증명하는 데 사용할 수 있습니다

$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY   = $FRIDAY, status = $?"
FRIDAY   = Fri, May 04, 2018  8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0

그리고

myfunc() {
    local x=$(echo "Foo"; true);  echo "x = $x -> $?"
    local y=$(echo "Bar"; false); echo "y = $y -> $?"
    echo -n "BUT! "
    local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}

$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1

다행스럽게도주택 검사오류를 잡아서 올려라SC2155, 이는 다음을 나타냅니다.

export foo="$(mycmd)"

로 변경해야합니다

foo=$(mycmd)
export foo

그리고

local foo="$(mycmd)"

로 변경해야합니다

local foo
foo=$(mycmd)

크레딧 및 참고자료

$(bar)$(quux)Gilles의 답변에서 명령 대체를 연결하는 아이디어를 얻었습니다. Pipefail과 비슷한 방식으로 백틱 실패 시 bash를 종료하려면 어떻게 해야 합니까?, 이 문제와 관련된 많은 정보가 포함되어 있습니다.

답변2

LESS=+/'^SIMPLE COMMAND EXPANSION' bashBash()에 문서화되어 있습니다.

확장 후에도 명령어 이름이 남아 있다면.... 그렇지 않으면 명령어가 종료됩니다. ...명령 대체 없이 명령은 상태 0으로 종료됩니다.

즉 (내 말은):

확장 후에 명령 이름이 남지 않고 명령 대체가 수행되지 않으면 명령줄은 0 상태로 종료됩니다.

관련 정보