이것은 bash의 버그입니까? 파이프에서 호출하면 'return'이 함수를 종료하지 않습니다.

이것은 bash의 버그입니까? 파이프에서 호출하면 'return'이 함수를 종료하지 않습니다.

최근 bash를 사용하면서 몇 가지 이상한 문제에 직면했습니다. 스크립트를 단순화하려고 노력하는 동안 다음과 같은 작은 코드 조각을 생각해 냈습니다.

$ o(){ echo | while read -r; do return 0; done; echo $?;}; o
0
$ o(){ echo | while read -r; do return 1; done; echo $?;}; o
1

return함수는 인쇄하지 않고 종료되어야 합니다 $?. 그렇지 않습니까? 좋아, 그런 다음 파이프에서 혼자 돌아올 수 있는지 확인했습니다.

$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script

루프 없이도 같은 일이 발생합니다 while.

$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed.

여기서 뭔가 빠졌나요? Google 검색 결과가 없습니다! 내 배쉬 버전은4.2.37(1)-릴리스데비안 Wheezy에서.

답변1

이것은 버그는 아니지만 bash,기록된 행동:

파이프라인의 각 명령은 자체 하위 셸에서 실행됩니다.

return명령은 함수 정의 내에서 유효하지만 서브쉘에서도 상위 쉘에 영향을 주지 않으므로 echo다음 명령은 관계없이 실행됩니다. 하지만 이동이 불가능한 주택건축이기 때문에POSIX 표준파이프라인으로 구성된 명령은 서브셸(기본값) 또는 최상위 수준(허용되는 확장)에서 실행될 수 있습니다.

또한 다중 명령 파이프라인의 각 명령은 하위 셸 환경에 있지만 확장으로 파이프라인의 일부 또는 모든 명령을 현재 환경에서 실행할 수 있습니다. 다른 모든 명령은 현재 쉘 환경 내에서 실행되어야 합니다.

bash다음 몇 가지 옵션을 통해 예상대로 행동할 수 있기를 바랍니다 .

$ set +m # disable job control
$ shopt -s lastpipe # do not run the last command of a pipeline a subshell 
$ o(){ echo | while read -r; do return 0; done; echo $?;}
$ o
$          <- nothing is printed here

답변2

관련된:https://stackoverflow.com/a/7804208/4937930

exit서브쉘을 통해 스크립트를 종료하거나 함수에서 복귀할 수 없는 것은 버그가 아닙니다 return. 다른 프로세스에서 실행되며 주 프로세스에는 영향을 주지 않습니다.

그 외에도 (아마도) 정의되지 않은 사양에서 bash의 문서화되지 않은 동작을 보게 될 것이라고 생각합니다. 함수 내에서 return하위 쉘 명령의 최상위 레벨은 오류를 주장하지 않으며 exit.

IMHO 이것은 bash 버그이며 return기본 명령문이 함수에 있는지 여부에 따라 일관되지 않게 동작합니다.

#!/bin/bash

o() {
    # Runtime error, but no errors are asserted,
    # each $? is set to the return code.
    echo | return 10
    echo $?
    (return 11)
    echo $?

    # Valid, each $? is set to the exit code.
    echo | exit 12
    echo $?
    (exit 13)
    echo $?
}
o

# Runtime errors are asserted, each $? is set to 1.
echo | return 20
echo $?
(return 21)
echo $?

# Valid, each $? is set to the exit code.
echo | exit 22
echo $?
(exit 23)
echo $?

산출:

$ bash script.sh 
10
11
12
13
script.sh: line 20: return: can only `return' from a function or sourced script
1
script.sh: line 22: return: can only `return' from a function or sourced script
1
22
23

답변3

POSIX 문서에 따르면,return외부 함수 또는 소스 스크립트 의 지정되지 않은 사용. 따라서 이를 처리하는 것은 쉘에 달려 있습니다.

SystemV 쉘은 대부분의 다른 POSIX 쉘 과 유사하게 ksh동작하는 오류를 보고합니다.returnexit힐리의 오쉬다음과 같은 동작도 있습니다.

$ for s in /bin/*sh /opt/schily/bin/osh; do
  printf '<%s>\n' $s
  $s -c '
    o(){ echo | while read l; do return 0; done; echo $?;}; o
  '
done
</bin/bash>
0
</bin/dash>
0
</bin/ksh>
</bin/lksh>
0
</bin/mksh>
0
</bin/pdksh>
0
</bin/posh>
0
</bin/sh>
0
</bin/yash>
0
</bin/zsh>
</opt/schily/bin/osh>
0

ksh그리고 zsh이러한 셸에 있는 파이프라인의 마지막 부분은 하위 셸이 아닌 현재 셸에서 실행되기 때문에 출력이 없습니다. return 문은 함수가 호출되는 현재 쉘 컨텍스트에 영향을 미치므로 함수가 아무것도 인쇄하지 않고 즉시 반환됩니다.

대화형 세션에서 bash오류를 보고하지만 셸을 종료하지 않으려면 schily's osh오류를 보고하고 셸을 종료하세요.

$ for s in /bin/*sh; do printf '<%s>\n' $s; $s -ci 'return 1; echo 1'; done
</bin/bash>
bash: return: can only `return' from a function or sourced script
1
</bin/dash>
</bin/ksh>
</bin/lksh>
</bin/mksh>
</bin/pdksh>
</bin/posh>
</bin/sh>
</bin/yash>
</bin/zsh>
</opt/schily/bin/osh>
$ cannot return when not in function

( 대화형 세션에서는 터미널이 종료되지 않고 오류가 보고되지만 쉘이 종료되지 않는다는 zsh출력이 표시됩니다 )bashyashschily's osh

답변4

보다 일반적인 대답은 bash와 일부 다른 쉘은 일반적으로 파이프의 모든 요소를 ​​별도의 프로세스에 배치한다는 것입니다. 명령 동작 시

계획 1|시나리오 2|옵션 3

어쨌든 프로그램은 일반적으로 별도의 프로세스에서 실행되기 때문입니다(당신이 그렇게 말하지 않는 한). 하지만 이는 놀라운 일이 될 수도 있습니다exec program

명령 1|명령 2|명령 3

이러한 명령 중 일부 또는 전부는 내장 명령입니다. 간단한 예는 다음과 같습니다:

$ a=0
$ echo | a=1
$ echo "$a"
0
$ cd /
$ echo | cd /tmp
$ pwd
/

조금 더 현실적인 예는 다음과 같습니다.

$ t=0
$ ps | while read pid rest_of_line
> do
>     : $((t+=pid))
> done
$ echo "$t"
0

전체... while루프가 하위 프로세스에 배치되므로 루프가 끝난 후 기본 쉘에서 해당 변경 사항을 볼 수 없습니다. 이것이 바로 여러분이 하고 있는 일입니다. 루프에 파이프를 연결하고 루프를 하위 쉘로 실행한 다음 하위 쉘에서 반환을 시도합니다.dodonetwhile

관련 정보