POSIX sh 조건에서 논리적 OR 평가의 예상치 못한 결과

POSIX sh 조건에서 논리적 OR 평가의 예상치 못한 결과

다음과 같은 if 문이 있다고 가정합니다.

if [ $(false) ]; then
    echo "?"
fi

그러면 "?"가 인쇄되지 않습니다(조건이 거짓임). 그런데 다음과 같은 경우에는 "?!"가 출력되는데 왜 그럴까요?

if [ $(false) -o $(false) ]; then
    echo "?!"
fi

답변1

$(false)false로 평가되지 않으며 빈 문자열을 생성합니다. 인용이 없기 때문에,

if [ $(false) ]; then

다음과 같이 평가됨

if [ ]; then

[빈 표현식은 거짓 이므로 이는 거짓입니다 .

if [ $(false) -o $(false) ]; then

다음과 같이 평가됨

if [ -o ]; then

이것아니요-o연산자를 사용하면 -o단일 문자열이 포함된 표현식으로 평가됩니다. [이러한 표현식은 true이므로 then문의 일부가 if실행됩니다.

바라보다POSIX 사양test, 특히:

연산자 우선 순위와 생성되어야 하는 반환 값을 결정하는 데 사용되는 알고리즘은 테스트할 인수 수를 기반으로 합니다. (단, "[...]" 형식을 사용하는 경우 마지막 매개변수 <오른쪽 대괄호>는 알고리즘에 포함되지 않아야 합니다.)

다음 목록에서 $1, $2, $3 및 $4는 테스트할 매개변수를 나타냅니다.

인수 0개:
false(1)를 종료합니다.
매개변수 1개:
$1이 null이 아니면 true(0)를 반환하고, 그렇지 않으면 false를 반환합니다.

test연산자는 두 개 이상의 인수가 제공되는 경우에만 고려됩니다.

명령의 종료 상태를 조건으로 사용하려면 명령 대체 또는 다음 항목에 넣지 마십시오 [ ].

if false; then

그리고

if false || false; then

또한 참고하세요test-a연산자는 -o더 이상 사용되지 않으며 신뢰할 수 없습니다.;쉘의 &&AND ||연산자를 사용해야 합니다.예를 들어

if [ "$a" = b ] || [ "$a" = c ]; then

답변2

훌륭한 답변에서 지적한 내용을 반복하지 않겠습니다.작성자: @StephenKitt그리고@ilkkachu여기서는 글쓰기의 의미에만 초점을 맞춥니다 if [ $(cmd) ]; then....

우선 이게 [아니라는 점 알아두세요POSIXsh연산자라고도 하는 별도의 유틸리티입니다.test, 여기서 문법의 //// 문 if부분 에 사용됩니다.ifthenelifelsefish언어.

그 임무는 인수를 조건식으로 평가하는 것입니다. 예를 들어 [, a, =, b]를 매개변수로 받거나 test, a, =및 을 받으면 b다음과 같이 해석하므로 false/실패한 종료 상태를 반환합니다."'a'와 'b'는 같은 문자열인가요?"조건식.

[일반적으로 // 명령문의 일부로 사용되거나 if// 또는 // 명령문 내의 유일한 명령으로 사용되지만 이러한 명령문일 필요도 없고 단일 명령을 호출할 필요도 없습니다.ifthenelifelsefiwhiledodoneuntildodone[

실행은 를 사용하고 , 결과를 확장 하고, 명령을 별도의 인수로 호출하는 것을 [ $(cmd) ]의미합니다 . 그런 다음 이러한 매개변수는 조건식으로 해석되고 결과에 따라 true 또는 false를 반환합니다(또는 표현식을 이해할 수 없는 경우 false).[[$(cmd)][

POSIX sh에서는 $(cmd)모든 후행 개행 문자를 제거하여 표준 출력으로 확장하며 cmd, 따옴표가 없고 목록 컨텍스트에 있으므로 IFS 분할 후 와일드카드가 적용됩니다(해당 출력에 NUL 문자가 있는 경우 동작은 지정되지 않습니다).

그래서 if [ $(cmd) ]또는 test $(cmd)아무 의미가 없습니다.

그것은 질문을 하는 것과 같습니다:"분할+글로브되면 출력이 cmdtrue로 평가되는 유효한 조건식을 구성합니까?"

예를 들어 cmd출력 합계에 우연히 포함되어 있고 a,*현재 작업 디렉터리에 두 개의 파일(하나는 이름이 지정 되고 다른 하나는 이름이 지정됨 ) 이 포함된 경우 해당 파일은 인수로 , , , 를 사용 하여 호출됩니다. 다행히 이는 유효한 조건식이지만 false를 반환하는 조건식 은 그렇지 않습니다 .$IFS,=b[ $(cmd) ][[a=b]ab

$ cd "$(mktemp -d)"
$ touch = b
$ cmd() { echo 'a,*'; }
$ IFS=,
$ set -o xtrace
$ if [ $(cmd) ]; then echo yes; else echo no; fi
++ cmd
++ echo 'a,*'
+ '[' a = b ']'
+ echo no
no

이제 [ "$(cmd)" ]더 의미가 있습니다. Quote 명령 대체는 NUL을 출력할 때 지정되지 않은 상태로 남아 있는 후행 개행 문자의 제거를 방지하지 않지만 cmd, 분할+glob 및 null 제거를 방지합니다. 따라서 [3개의 인수가 항상 전달됩니다: , , [후행 줄 바꿈이 제거된 출력 및 . 옆에는 해당 매개변수가 빈 문자열이 아닌 경우에만 true를 반환하는 매개변수가 있습니다. 이는 와 동일합니다.cmd][][[ -n "$(cmd)" ]

그래서 그것은 다음과 같이 말하는 것과 같습니다:" cmd개행 문자(또는 NUL)가 아닌 문자를 하나 이상 출력하십시오.". 텍스트를 출력하는 명령의 경우(텍스트가 줄로 구성되고 NUL을 포함하지 않음이 보장됨)cmd" 공백이 아닌 라인을 하나 이상 출력 하시겠습니까 ?"

짧은 출력을 제외하면 전체 출력을 읽고 에 전달하기 전에 메모리에 저장하게 되므로 비효율적인 방식으로 이 작업을 수행합니다 [.

if cmd | grep -q .; then...

grep종료되므로 더 효율적입니다.진짜적어도 하나의 문자를 포함하는 줄을 찾으면.

그렇다면 결국 쉘이 충돌하게 됩니다 cmd.yesif [ "$(cmd)" ]충분한 저장오류가 발생하면 if cmd | grep -q .yes가 즉시 출력됩니다( yesSIGPIPE 신호에 의해 종료됩니다).

성공 여부 를 테스트하려면 cmd출력 여부에 관계없이 다음을 수행하면 됩니다.

if cmd; then
  echo cmd succeeded
else
  echo cmd failed
fi

if $(cmd); then말도 안되는 또 다른 코드 예제입니다. 그러나 cmd존재하는 예는 조금 다릅니다 false.

다시 말하지만, $(cmd)후행 개행 문자는 제거되어 분할+글로브의 영향을 받습니다. 그러나 이번에는 생성된 단어가 [/ test유틸리티에 전달되지 않고 이러한 단어 중 첫 번째 단어(있는 경우)가 실행될 명령으로 처리되고 모든 단어가 인수로 전달됩니다.

따라서 cmd출력 echo:hello:world$IFS포함이 인수 로 및 인수로 실행 되고 stdout에 성공적으로 쓸 수 있다고 가정하면 true :/ 성공을 반환하므로 해당 부분 이 실행됩니다.echoechohelloworldecho"hello world\n"then

이제 cmd출력이 생성되지 않거나 줄 바꿈 및 IFS 공백 문자만 생성되면 $(cmd)단어가 전혀 생성되지 않으므로 다른 명령은 실행되지 않으며 이 시점에서는 cmd명령문의 종료 상태가 중요합니다 if.

존재하다:

if $(false); then
  echo yes
else
  echo no
fi

no출력이 false생성되지 않았기 때문에 명령이 실행되지 않았기 때문에 출력되고 종료 상태 에 따라 해당 섹션이 실행되는지 여부 false가 결정됩니다 .else

존재하다:

if $(echo true; false); then
  echo yes
else
  echo no
fi

, 또는 문자를 포함 $IFS하지 않는 것으로 가정되며 결국 실행되고 종료 상태가 명령문 내의 분기를 결정합니다.truetrueif

관련 정보