괄호는 실제로 명령을 서브 쉘에 넣습니까?


94

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

x=1

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

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

답변:


134

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

일부 쉘은 서브 쉘에서 일부 변수를 변경합니다. Bash는 BASHPID쉘 프로세스의 PID로 설정 되며 이는 서브 쉘에서 변경됩니다. Bash, zsh 및 mksh $RANDOM는 상위 및 서브 쉘에서 서로 다른 값을 산출합니다. 그러나 이와 같은 내장 특수한 경우를 제외하고 모든 변수는 서브 쉘에서 원래 쉘과 동일한 값, 동일한 내보내기 상태, 동일한 읽기 전용 상태 등을 갖습니다. 모든 함수 정의, 별명 정의, 쉘 옵션 및 다른 설정도 상속됩니다.

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

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

그러므로:

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) 원래 쉘의 트랩을보고 할 수 있습니다. 또한 많은 쉘에는 트랩과 관련된 코너 케이스에 버그가 있습니다. 예를 들어 ninjalj 는 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) )'실행 ERR하지만 ERR중간 서브 쉘 의 트랩은 실행 하지 않습니다. — set -E옵션은ERR모든 서브 쉘에 트랩하지만 중간 서브 쉘은 최적화되어 ERR트랩 을 실행할 필요가 없습니다 .


2
@Kusalananda No. ( x=out; (x=in; echo $x))
Gilles

2
@ flow2k 이것은 같은 수준에서 일어나는 일들에 대한 확장 순서입니다. 그러나 확장이 평가와 어떻게 혼합되는지 고려해야합니다. 확장에 중첩 구조의 평가가 필요한 경우 내부 구조가 먼저 평가됩니다. 예를 들어, 평가 echo $(x=2; echo $x)하려면 조각 $(x=2; echo $x)을 확장해야합니다. 이를 위해서는 명령을 평가해야합니다 x=2; echo $x. $x부품 평가 후이 평가 중에 확장이 발생합니다 x=2.
Gilles

2
@ flow2k 매개 변수 확장과 명령 대체 사이에는 순서가 없습니다. 이 문장은 세미콜론을 사용하여 확장 단계를 분리하지만 매개 변수 확장 및 명령 대체는 동일한 세미콜론으로 구분 된 절에 있습니다 (예, 미묘합니다). 순서는 부품 중 하나가 다른 부품에 영향을 미치는 부작용이있을 때 중요합니다 (예 : ( x설정되지 않은 상태) echo $(echo foo >somefile)${x-$(cat somefile)}또는) echo $(echo $x),${x=1}.
Gilles

1
@ 길; 혼란 스러워요. 서브 쉘이 스크립트를 실행하는 것과 다른 경우 왜 쉘 스크립트를 실행하면 서브 쉘이라는 새로운 프로세스가 시작됩니다. ? 또한 서브 쉘 환경은 쉘 환경의 복제본으로 작성되어야합니다 . 따라서 ./file은 서브 쉘 환경에서 실행되므로 변수 지정으로 설정된 쉘 매개 변수를 상속해야합니다.
haccks

2
@haccks ABS의 정의는 근사치이며 그리 좋은 것은 아닙니다. 예제는 좋지만 해당 페이지의 처음 두 줄은 너무 단순화되어 잘못되었습니다. 다른 스크립트에서 스크립트를 실행 하면 서브 쉘 이 아닌 새 프로세스가 시작 됩니다. SUS에서는 정의가 정확하지만 항상 이해하기 쉽지는 않습니다. ./file서브 쉘에서 실행되지 않습니다. 도 참조 unix.stackexchange.com/q/261638unix.stackexchange.com/a/157962

15

물론 모든 문서에서 알 수 있듯이 괄호로 묶인 명령은 서브 쉘에서 실행됩니다.

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

ksh 맨 페이지는 이것을 bash보다 조금 더 명확하게 만듭니다.

man ksh:

반출되지 않은 변수를 제거하지 않고 서브 쉘에서 괄호로 묶인 명령이 실행됩니다.

man bash:

(명부)

리스트는 서브 쉘 환경에서 실행됩니다 (아래의 명령 실행 환경 참조). 쉘 환경에 영향을주는 변수 할당 및 내장 명령은 명령이 완료된 후에도 유효하지 않습니다.

명령 실행 환경

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


3
이것은 내 보내지 않으면 -script가 아무 것도 인쇄하지 않는 이유를 설명 When a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following.하는 항목 · shell variables and functions marked for export, along with variables exported for the command, passed in the environment((같은 man bash섹션에서) 포함) 과 대조됩니다 . echo $xx
Johan E
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.