POSIX 셸 스크립트 파이프라인에서 실행된 마지막 함수가 변수 값을 유지하지 않는 이유는 무엇입니까?

POSIX 셸 스크립트 파이프라인에서 실행된 마지막 함수가 변수 값을 유지하지 않는 이유는 무엇입니까?

다음 스크립트를 가정합니다.

#!/bin/sh

func1() {
  eval $1'=$(cat)'
  eval echo "Value$2 inside function : \$$1"
}

func1 x 1 <<'HEREDOC'
Hello World
HEREDOC

echo "Value1 outside function: $x"

x=""

echo "Hello World" | func1 x 2

echo "Value2 outside function: $x"

Bash 4.3.43-4.fc25에서 출력은 다음과 같습니다.

Value1 inside function : Hello World
Value1 outside function: Hello World
Value2 inside function : Hello World
Value2 outside function: 

bashism을 사용하여 shopt -s lastpipe마지막 줄에 "Hello World"도 표시하도록 했지만 직관적으로는 이것이 자동으로 발생하지 않는 이유를 이해할 수 없습니다. 이것이 예상되는가?

POSIX 표준은 실제로 이 주제를 논의하지 않는 것 같습니다.http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_02

답변1

역사적으로,본 쉘그리고코헨 쉘다르게 행동하십시오. 파이프의 양쪽이 서로 다른 프로세스에서 병렬로 실행되기 때문에 나머지 스크립트 전체에서 양쪽의 변수 할당을 유지하는 것은 불가능합니다. 주어진 경우 unset a; a=b | a=c변수는 a설정되지 않거나 같 b거나 같을 수 c있습니다 .bc

Bourne 셸에서 파이프 연산자의 각 측면은 하위 셸(즉, 별도의 셸 프로세스)에서 실행되므로 변수 할당이 유지되지 않습니다. Korn 쉘에서 파이프의 오른쪽은 원래 쉘 프로세스에서 실행됩니다.

POSIX는 기존 동작이 다른 많은 경우처럼 두 가지 동작을 모두 허용합니다. 이 섹션에 설명된 대로"쉘 실행 환경":

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

일부 시스템은 이미 현재 환경에서 파이프라인의 최종 단계를 구현하여 다음과 같은 명령을 수행합니다.

command | read foo

foo현재 환경에서 변수를 설정합니다. 이 확장은 허용되지만 필수는 아닙니다. 따라서 쉘 프로그래머는 파이프가 서브쉘 환경에 있지만 이에 의존해서는 안 된다는 점을 고려해야 합니다.

실제로 생성된 프로세스 수를 주의 깊게 살펴보지 않는 한 동일하게 작동하는 일부 최적화를 제외하고 마지막 명령 이외의 명령은 항상 하위 쉘에서 실행됩니다. 즉, 쉘에는 unset a; a=b | true; echo $aprint 이 없습니다 b.

ash, dash, bash, pdksh 및 mksh와 같은 대부분의 다른 Bourne 유사 쉘은 Bourne 쉘처럼 작동합니다. Zsh는 Korn 쉘처럼 동작합니다. 최신 버전의 bash는 ksh 동작으로의 전환을 사용할 수 있습니다 shopt -s lastpipe.

답변2

예, POSIX는 이 주제를 다루고 있습니다. ~에서여기:

서브쉘 환경은 쉘 환경의 복사본으로 생성되어야 하지만 무시되지 않는 신호 트랩은 기본 동작으로 설정되어야 합니다. 서브쉘 환경에 대한 변경사항은 쉘 환경에 영향을 주어서는 안됩니다. 명령 대체, 괄호로 그룹화된 명령 및 비동기 목록은 서브쉘 환경에서 실행되어야 합니다. 또한 다중 명령 파이프라인의 각 명령은 하위 셸 환경에 있지만 확장으로 파이프라인의 일부 또는 모든 명령을 현재 환경에서 실행할 수 있습니다. 다른 모든 명령은 현재 쉘 환경 내에서 실행되어야 합니다.

기본 동작을 선택하는 것은 셸에 달려 있습니다. 존재하다:

echo 1 | read x
echo "$x"

only zshksh출력 1, 다른 현대 Bourne 유사 쉘은 빈 문자열을 출력합니다.

답변3

POSIX 라고:

다중 명령 파이프라인의 각 명령은 서브셸 환경에 위치합니다.

그 다음에

서브셸 환경에 대한 변경 사항은 셸 환경에 영향을 주어서는 안 됩니다.

파이프라인을 구현하는 가장 간단한 방법(호출)을 고려한다면 이는 의미가 있습니다.pipe(), 서브쉘을 분기하고 한쪽에서 stdout/input을 바꾼 다음 서브쉘에서 명령을 실행합니다. 이것은 또한Bash에서 정의된 동작.

당신은 할 수 없습니다의지하다그러나 이런 일이 발생하면

그러나 확장을 통해 파이프라인의 일부 또는 모든 명령을 현재 환경에서 실행할 수 있습니다.

이것이 lastpipe바로 이 사건이 하는 일입니다. 일부 다른 셸에서는 이것이 특정 상황의 기본 동작입니다.

관련 정보