괄호는 실제로 명령을 서브쉘에 넣나요?

괄호는 실제로 명령을 서브쉘에 넣나요?

내가 읽은 바에 따르면 명령을 괄호 안에 넣으면 스크립트를 실행하는 것과 유사하게 하위 쉘에서 실행되어야 합니다. 이것이 사실이라면 x를 내보내지 않으면 변수 x를 어떻게 볼 수 있습니까?

x=1

(echo $x)명령줄에서 실행하면 1이 됩니다.

echo $x스크립트를 실행하면 예상대로 아무것도 발생하지 않습니다.

답변1

서브쉘은 원본 쉘 프로세스와 거의 동일한 복사본으로 시작됩니다. 후드 아래에서 쉘은 다음을 호출합니다.fork코드와 메모리가 복사본인 새 프로세스를 생성하는 시스템 호출 1 2 . 하위 셸이 생성되면 해당 하위 셸과 상위 셸 사이에는 거의 차이가 없습니다. 특히, 그들은 동일한 변수를 가지고 있습니다. 특수 변수 도 $$하위 쉘에서 동일한 값을 유지합니다. 이는 원래 쉘의 프로세스 ID입니다. 마찬가지로 $PPID원래 쉘의 상위 PID도 마찬가지입니다.

몇 개의 쉘은 서브쉘의 몇 가지 변수를 변경합니다. Bash ≥4.0은 BASHPID서브쉘에서 변경되는 쉘 프로세스의 PID로 설정됩니다. Bash, zsh 및 mksh는 $RANDOM상위 쉘과 하위 쉘에서 서로 다른 값을 생성하도록 준비합니다 . 그러나 이와 같이 내장된 특별한 경우를 제외하고 모든 변수는 원본 셸에서와 동일한 값, 동일한 내보내기 상태, 동일한 읽기 전용 상태 등을 하위 셸에서 갖습니다. 모든 함수 정의, 별칭 정의, 셸 옵션 및 다른 설정도 상속됩니다.

에 의해 생성된 서브쉘은 (…)생성자와 동일한 파일 설명자를 갖습니다. 서브쉘을 생성하는 다른 방법은 사용자 코드를 실행하기 전에 일부 파일 설명자를 수정합니다. 예를 들어 파이프의 왼쪽은 표준 출력이 파이프에 연결된 하위 쉘 3 에서 실행됩니다. 서브쉘은 또한 동일한 현재 디렉토리, 동일한 신호 마스크 등으로 시작합니다. 몇 가지 예외 중 하나는 서브쉘이 사용자 정의 트랩을 상속하지 않는다는 것입니다: 무시된 신호(trap '' SIGNAL )는 서브쉘에서 무시된 상태로 유지되지만 다른 트랩(trap CODE신호)은 기본 동작 4 로 재설정됩니다 .

따라서 서브쉘은 스크립트 실행과 다릅니다. 스크립트는 별도의 프로그램입니다. 이 별도의 프로그램은 우연히도 상위 프로그램과 동일한 인터프리터에 의해 실행되는 스크립트일 수도 있지만 이러한 우연이 별도의 프로그램에 상위 프로그램의 내부 데이터에 대한 특별한 가시성을 제공하지는 않습니다. 내보내지 않은 변수는 내부 데이터이므로 하위 쉘 스크립트에 대한 인터프리터가처형된, 이러한 변수는 표시되지 않습니다. 내보낸 변수, 즉 환경 변수는 실행된 프로그램으로 전송됩니다.

따라서:

x=1
(echo $x)

1서브쉘은 자신을 생성한 쉘의 복제이기 때문에 인쇄됩니다 .

x=1
sh -c 'echo $x'

쉘을 쉘의 하위 프로세스로 실행하지만 x두 번째 줄의 프로세스는 x두 번째 라인의 프로세스와 더 이상 연결되지 않습니다.

x=1
perl -le 'print $x'

또는

x=1
python -c 'print x'

1 셸이 분기를 최적화하지 않는 한 실행 중인 코드의 동작을 유지하는 데 필요한 만큼 분기를 에뮬레이트합니다. Ksh93은 많이 최적화하지만 다른 쉘은 대부분 그렇지 않습니다.
2 의미상으로는 복사본입니다. 구현 관점에서 볼 때 많은 공유가 진행되고 있습니다.
3 오른쪽의 경우 쉘에 따라 다릅니다.
4 이것을 테스트해 보면 다음 사항에 유의하십시오.같은 것들$(trap)원래 쉘의 트랩을 보고할 수 있습니다. 또한 많은 껍질에는 함정과 관련된 코너 케이스에 버그가 있다는 점에 유의하십시오. 예를 들어닌자잘즈bash 4.3부터 "두 개의 하위 쉘"의 경우 중첩된 하위 쉘에서 트랩을 bash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'실행 하지만 중간 하위 쉘에서는 트랩을 실행하지 않습니다. 옵션은 트랩을 모든 하위 쉘에 전파해야 하지만 중간 하위 쉘은 최적화되어 있으므로 그렇지 않습니다. 함정 을 실행할 곳이 없습니다 .ERRERRset -EERRERR

답변2

분명히 그렇습니다. 모든 문서에 나와 있듯이 괄호로 묶인 명령은 하위 쉘에서 실행됩니다.

하위 쉘은 모든 상위 변수의 복사본을 상속합니다. 차이점은 서브쉘에서 변경한 내용이 상위 쉘에서도 적용되지 않는다는 것입니다.

ksh 매뉴얼 페이지는 이것을 bash 매뉴얼 페이지보다 조금 더 명확하게 만듭니다:

man ksh:

내보내지 않은 변수를 제거하지 않고 하위 셸에서 괄호로 묶인 명령이 실행됩니다.

man bash:

(목록)

목록은 서브쉘 환경에서 실행됩니다(아래 명령 실행 환경 참조). 쉘 환경에 영향을 미치는 변수 할당 및 내장 명령은 명령이 완료된 후에는 적용되지 않습니다.

명령 실행 환경

쉘에는 다음으로 구성된 실행 환경이 있습니다. [...] 변수 할당에 의해 설정되는 쉘 매개변수 [...].
명령 대체, 괄호로 그룹화된 명령 및 비동기 명령은 쉘 환경과 복제된 서브쉘 환경에서 호출됩니다. [...]

관련 정보