[unix] 이것은 bash의 버그입니까? `return`은 파이프에서 호출 된 경우 기능을 종료하지 않습니다

요즘 배쉬에 이상한 문제가 있습니다. 스크립트를 단순화하려고 할 때이 작은 코드 조각을 생각해 냈습니다.

$ 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.

내가 여기서 놓친 것이 있습니까? 구글 검색은 이것에 대해 아무것도 가져 오지 않았다! 내 bash 버전은 Debian Wheezy의 4.2.37 (1) 릴리스 입니다.



답변

관련 : /programming//a/7804208/4937930

스크립트를 종료하거나 함수에서 exit또는 return서브 쉘로 리턴 할 수없는 것은 버그가 아닙니다 . 다른 프로세스에서 실행되며 기본 프로세스에 영향을 미치지 않습니다.

그 외에도, 정의되지 않은 스펙에서 아마도 문서화되지 않은 bash 동작을보고 있다고 가정합니다. 함수에서는 return최상위 레벨의 서브 쉘 명령에서 오류가 발생하지 않으며 다음 과 같이 작동 exit합니다.

IMHO 그것은 return주요 진술이 함수에 있는지 아닌지 에 따라 일관성이없는 행동에 대한 bash 버그입니다 .

#!/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


답변

버그는 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


답변

POSIX 문서 당, 사용 return기능의 외부 또는 소스 스크립트은 지정되지 않습니다 . 따라서 처리 할 쉘에 따라 다릅니다.

시스템 V 쉘에있는 동안, 오류를보고합니다 ksh, return같은 기능의 외부 또는 소스 스크립트 동작합니다 exit. 대부분의 다른 POSIX 쉘과 schily의 osh 도 다음과 같이 작동합니다.

$ 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대화 형 세션 및 출력 단자 할 일이 종료되지 않습니다 bash, yash그리고 schily's osh오류를보고하지만, 쉘을 종료하지 않았다)


답변

bash에서 파이프 라인의 각 명령이 하위 셸에서 실행되는 것으로 예상되는 동작이 있다고 생각합니다. 함수의 전역 변수를 수정하려고 시도하면 직접 대화 할 수 있습니다.

foo(){ x=42; : | x=3; echo "x==$x";}

그건 그렇고, 반환은 작동하지만 서브 쉘에서 반환됩니다. 다시 확인할 수 있습니다.

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

다음을 출력합니다 :

1
This should not be printed.

따라서 return 문이 서브 쉘을 올바르게 종료했습니다

.


답변

더 일반적인 대답은 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

전체가 여기서 whiledodone루프 서브 프로세스에 투입되고, 그 변화되도록하는 t루프 종료 후 주 셀에 표시되지 않는다. 이것이 바로 여러분이하는 일입니다. while루프 로 파이핑하여 루프가 서브 쉘로 실행 된 다음 서브 쉘에서 복귀하려고합니다.


답변