Bash에서 서브 쉘을 호출하기위한 규칙?


24

서브 쉘 작성에 대한 Bash 규칙을 오해하는 것 같습니다. 괄호는 항상 자체 프로세스로 실행되는 서브 쉘을 생성한다고 생각했습니다.

그러나 이것은 사실이 아닙니다. 코드 스 니펫 A (아래)에서 두 번째 sleep명령은 pstree다른 터미널에서 결정된 별도의 셸에서 실행되지 않습니다 . 그러나 코드 조각 B에서 두 번째 sleep명령 별도의 셸에서 실행됩니다. 스 니펫의 유일한 차이점은 두 번째 스 니펫에는 괄호 안에 두 개의 명령이 있다는 것입니다.

서브 쉘 작성시기에 대한 규칙을 누군가가 설명해 주시겠습니까?

코드 스 니펫 A :

sleep 5
(
sleep 5
)

코드 스 니펫 B :

sleep 5
(
x=1
sleep 5
)

답변:


20

괄호는 항상 서브 쉘을 시작합니다. 무슨 일이 일어나고 있는지 bash는 sleep 5해당 하위 쉘이 마지막으로 실행 한 명령 인 것을 감지 하므로 + exec대신 호출 합니다 . 이 명령은 동일한 프로세스에서 서브 쉘을 대체합니다.forkexecsleep

다시 말해 기본 사례는 다음과 같습니다.

  1. ( … )서브 쉘을 작성하십시오. 원래 프로세스는 fork및을 호출합니다 wait. 하위 프로세스 인 하위 프로세스에서 :
    1. sleep하위 프로세스의 하위 프로세스가 필요한 외부 명령입니다. 서브 쉘은 fork및을 호출합니다 wait. 하위 프로세스에서 :
      1. 하위 프로세스는 외부 명령 →을 실행합니다 exec.
      2. 결국 명령이 종료됩니다 → exit.
    2. wait 서브 쉘에서 완료됩니다.
  2. wait 원래 프로세스에서 완료됩니다.

최적화는 다음과 같습니다.

  1. ( … )서브 쉘을 작성하십시오. 원래 프로세스는 fork및을 호출합니다 wait. 서브 프로세스에서 호출 할 때까지 서브 쉘입니다 exec.
    1. sleep 외부 명령이며이 프로세스가 마지막으로 수행해야하는 작업입니다.
    2. 하위 프로세스는 외부 명령 →을 실행합니다 exec.
    3. 결국 명령이 종료됩니다 → exit.
  2. wait 원래 프로세스에서 완료됩니다.

를 호출 한 후 다른 것을 추가 sleep하면 서브 쉘을 유지해야하므로이 최적화를 수행 할 수 없습니다.

를 호출하기 전에 다른 것을 추가 sleep하면 최적화가 가능하지만 ksh가 수행 할 수는 있지만 bash는 수행하지 않습니다 (이 최적화와 매우 보수적입니다).


를 호출 fork하여 서브 쉘을 작성하고를 호출 하여 하위 프로세스를 작성합니다 (외부 명령을 실행하기 위해)fork + exec . 그러나 첫 번째 단락은 fork + exec하위 쉘도 필요 하다는 것을 암시합니다 . 내가 여기서 잘못하고있는 것은 무엇입니까?
haccks

1
@haccks fork+ exec는 서브 쉘을 위해 호출되지 않고 외부 명령을 위해 호출됩니다. 최적화가 없으면 fork서브 쉘을 호출하고 외부 명령을 호출해야합니다. 내 답변에 자세한 흐름 설명을 추가했습니다.
Gilles 'SO- 악마 그만해'

업데이트 주셔서 감사합니다. 이제 더 잘 설명합니다. 나는 (...)(기본 경우) exec서브 쉘이 실행할 외부 명령이 있는지 여부 에 따라 호출이있을 수도 있고 없을 수도 있다고 추론 할 수 있지만 외부 명령을 실행하는 경우에는 있어야합니다 fork + exec.
haccks

또 다른 질문 :이 최적화는 서브 쉘에서만 작동 date합니까, 아니면 쉘에서 와 같은 명령 에서 수행 될 수 있습니까?
haccks

@ haccks 질문을 이해하지 못합니다. 이 최적화는 쉘 프로세스가 마지막으로하는 것처럼 외부 명령을 호출하는 것입니다. 하위 쉘에만 국한되지는 않습니다 : 비교 strace -f -e clone,execve,write bash -c 'date'strace -f -e clone,execve,write bash -c 'date; true'
Gilles 'SO-stop

4

로부터 고급 Bash 프로그래밍 가이드 :

"일반적으로 스크립트의 외부 명령은 하위 프로세스를 분기하지만 Bash 내장 기능은 그렇지 않습니다. 이러한 이유로 내장 명령은 외부 명령에 비해 시스템 자원을 더 빠르게 실행하고 더 적게 사용합니다."

그리고 조금 더 아래로 :

"괄호 안에 포함 된 명령 목록은 서브 쉘로 실행됩니다."

예 :

[root@talara test]# echo $BASHPID
10792
[root@talara test]# (echo $BASHPID)
4087
[root@talara test]# (echo $BASHPID)
4088
[root@talara test]# (echo $BASHPID)
4089

OP 코드를 사용하는 예 (참을성이 없어 잠이 짧음) :

echo $BASHPID

sleep 2
(
    echo $BASHPID
    sleep 2
    echo $BASHPID
)

출력 :

[root@talara test]# bash sub_bash
6606
6608
6608

2
답장을 보내 주셔서 감사합니다. Tim. 그래도 내 질문에 완전히 대답하지는 못합니다. "괄호 사이에 포함 된 명령 목록 sleep은 하위 셸로 실행 "이므로 두 번째 는 하위 셸에서 실행될 것으로 예상됩니다 (서브 셸의 하위 프로세스가 아니라 기본 제공되므로 하위 셸의 프로세스에서). 그러나 어쨌든 부모 Bash 프로세스 아래에 Bash 하위 프로세스와 같은 하위 셸이 존재할 것으로 기대합니다. 위의 스 니펫 B의 경우에는 그렇지 않습니다.
bashful

수정 : sleep내장 된 것처럼 보이지 않기 때문에 sleep두 스 니펫 의 두 번째 호출이 서브 쉘 프로세스의 서브 프로세스에서 실행될 것으로 예상합니다 .
bashful 2016 년

@bashful $BASHPID변수를 사용 하여 코드를 해킹 할 자유를 얻었습니다 . 슬프게도 당신이하고있는 방식은 당신에게 내가 믿는 모든 이야기를주지 못했습니다. 답변에서 추가 된 출력을 참조하십시오.
Tim

4

@Gilles 답변에 대한 추가 메모.

Gilles가 말했듯이 : The parentheses always start a subshell.

그러나 이러한 하위 셸의 숫자는 다음과 같이 반복 될 수 있습니다.

$ (echo "$BASHPID and $$"; sleep 1)
2033 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2040 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2047 and 31679

보시다시피 $$는 계속 반복되며 이는 올바른 man bash행 을 찾기 위해이 명령을 실행하기 때문입니다 .

$ LESS=+/'^ *BASHPID' man bash

BASHPID
현재 bash 프로세스의 프로세스 ID로 확장됩니다. 이것은 bash를 다시 초기화 할 필요가없는 서브 쉘과 같은 특정 상황에서 $$와 다릅니다.

즉, 쉘이 다시 초기화되지 않으면 $$는 동일합니다.

또는 이것으로 :

$ LESS=+/'^ *Special Parameters' man bash

특수 매개 변수
$ 쉘의 프로세스 ID로 확장됩니다. () 서브 쉘에서는 서브 쉘이 아닌 현재 쉘의 프로세스 ID로 확장됩니다.

$$현재 쉘 (아닌 서브 쉘)의 ID이다.


1
특정 섹션에서 bash 맨 페이지를 여는 좋은 방법
Daniel Serodio
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.