요즘 배쉬에 이상한 문제가 있습니다. 스크립트를 단순화하려고 할 때이 작은 코드 조각을 생각해 냈습니다.
$ 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
전체가 여기서 while
… do
… done
루프 서브 프로세스에 투입되고, 그 변화되도록하는 t
루프 종료 후 주 셀에 표시되지 않는다. 이것이 바로 여러분이하는 일입니다. while
루프 로 파이핑하여 루프가 서브 쉘로 실행 된 다음 서브 쉘에서 복귀하려고합니다.