Bash에서 서브쉘을 호출하는 규칙은 무엇입니까?

Bash에서 서브쉘을 호출하는 규칙은 무엇입니까?

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

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

서브쉘 생성 규칙을 설명해 줄 수 있는 사람이 있나요?

코드 조각 A:

sleep 5
(
sleep 5
)

코드 조각 B:

sleep 5
(
x=1
sleep 5
)

답변1

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

즉, 기본 사례는 다음과 같습니다.

  1. ( … )서브셸을 만듭니다. 원래 프로세스는 fork및 를 호출합니다 wait. 하위 프로세스에서 이는 서브쉘입니다.
    1. sleep하위 프로세스의 하위 프로세스가 필요한 외부 명령입니다. 서브쉘 호출 forkwait. 하위 하위 프로세스에서:
      1. 하위 하위 프로세스는 외부 명령 → 을 실행합니다 exec.
      2. 최종 명령이 종료됩니다 → exit.
    2. wait서브셸에서 수행됩니다.
  2. wait원래 프로세스로 완료되었습니다.

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

  1. ( … )서브셸을 만듭니다. 원래 프로세스는 fork및 를 호출합니다 wait. 하위 프로세스 내에서는 다음을 호출할 때까지 하위 쉘입니다 exec.
    1. sleep외부 명령이며 프로세스가 수행해야 하는 마지막 작업입니다.
    2. 하위 프로세스는 외부 명령 → 을 실행합니다 exec.
    3. 최종 명령이 종료됩니다 → exit.
  2. wait원래 프로세스로 완료되었습니다.

호출 후에 다른 것을 추가하는 경우 sleep이러한 최적화가 발생하지 않도록 서브쉘을 보존해야 합니다.

호출 전에 다른 것을 추가하면 최적화가 이루어질 수 있지만 sleep(ksh가 이를 수행함) bash는 그렇지 않습니다(이 최적화는 매우 보수적입니다).

답변2

~에서고급 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

답변3

@Gilles의 답변에 대한 추가 설명입니다.

Giles가 말했듯이 :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 입니다 .

관련 정보