최근 셸 스크립트를 작성하던 중 Bash 특수 변수인 $$와 $BASHPID가 서로 다르게 동작한다는 점을 발견했습니다. 리눅스에서 실행되는 모든 프로세스에는 프로세스 ID(PID)가 할당되며, 운영체제는 바로 이 ID를 통해 각 프로세스를 식별하고 관리합니다.
현재 셸의 프로세스 ID 확인하기
마찬가지로 Bash 터미널 세션에도 고유한 프로세스 ID가 부여됩니다. 현재 셸의 프로세스 ID를 저장하는 특수 변수로 "$$"와 "$BASHPID"가 있는데요. 아래 명령어를 실행해 현재 셸의 프로세스 ID를 직접 확인해 보세요. 메인 셸에서는 두 변수 모두 동일한 값을 반환합니다.
$ echo $$ # 특수 변수 $$ 출력 $ echo $BASHPID # 변수 $BASHPID 출력
서브셸(자식 프로세스) 생성 과정 이해하기
Bash에서 셸로부터 외부 프로그램을 호출하면 자식 프로세스(서브셸)가 생성되고, 해당 프로그램은 오직 이 자식 프로세스 안에서만 실행됩니다. 아래 예시는 부모 셸이 프로그램을 실행하기 위해 서브셸을 생성하는 과정을 보여주기 위해, 간단한 프로세스 모니터링 명령어를 담은 "sample.sh"라는 스크립트입니다.
#!/usr/bin/env bash ps -ef --forest | grep -i bash
이 스크립트를 실행하면 bash의 프로세스 구조를 확인할 수 있습니다. 아래 이미지를 보면 스크립트 호출 시 bash가 자식 프로세스를 생성하고, 그 안에서 스크립트를 실행하는 것을 알 수 있습니다.
$ ./sample.sh
스크립트 내에서 $$와 $BASHPID 비교하기
이번에는 스크립트 안에서 "$$"와 "$BASHPID"를 함께 출력해 어떤 값이 반환되는지 살펴보겠습니다.
#!/usr/bin/env bash echo "============================" ps -ef --forest | grep -i bash echo "============================" echo "PID USING $ FOR SCRIPT $0 ==> $$" echo "PID USING BASHPID FOR SCRIPT $0 ==> $BASHPID" echo
다시 스크립트를 실행합니다.
$ ./sample.sh
결과적으로 두 변수 모두 동일한 프로세스 ID를 반환했습니다. 그렇다면 진짜 차이는 어디서 드러날까요? 괄호 () 안에 명령어를 넣어 스크립트 내부에 또 하나의 자식 프로세스를 만들어 보겠습니다.
괄호()로 서브셸을 만들 때 나타나는 차이
# PID를 변수에 저장... VAR_HASH=$(echo $$) VAR_BASHPID=$(echo $BASHPID) echo "VALUE OF VAR_HASH ==> $VAR_HASH" echo "VALUE OF VAR_BASHPID ==> $VAR_BASHPID"
Bash에서 괄호(Parentheses)는 자식 프로세스를 호출하고 괄호 안의 내용을 실행합니다. 따라서 $$와 $BASHPID 모두 새로운 자식 프로세스 ID를 저장해야 할 것 같지만, 위 결과에서 뚜렷한 차이를 확인할 수 있습니다. $$는 부모 프로세스 ID, 즉 스크립트 sample.sh의 프로세스 ID인 382를 저장한 반면, $BASHPID는 괄호가 새로 생성한 자식 프로세스의 ID를 저장했습니다.
매뉴얼(man page)로 동작 원리 파악하기
이제 이 동작 방식이 왜 그런지 이해해 보겠습니다. Bash 매뉴얼 페이지에서 어떻게 설명하는지 확인해 보겠습니다.
$ man bash
정리하면 다음과 같습니다. $$를 사용하면 서브셸 안에서도 자신이 생성된 부모 프로세스의 ID를 가리킵니다. 반면 BASHPID는 항상 현재 프로세스의 ID를 저장합니다. 즉, 괄호 안에서 호출하면 자식 프로세스의 ID가 반환되는 것입니다.
변수 재할당과 unset 동작의 차이
$$ 변수는 값을 할당하거나 수정할 수 없습니다. 반면 BASHPID는 재할당 자체는 가능하지만 아무런 효과가 없습니다.
$ $=10 $ BASHPID=10 $ echo $BASHPID
흥미로운 점은 BASHPID를 unset으로 해제할 수 있다는 것입니다. unset하면 특수 변수로서의 상태를 잃고, 이후에는 일반 변수처럼 자유롭게 사용할 수 있게 됩니다.
$ unset BASHPID $ echo $BASHPID $ BASHPID="Tecmint" $ echo $BASHPID
unset 이후에는 셸의 프로세스 ID를 다시 할당하더라도 이미 특수 상태를 잃었기 때문에 일반적인 사용자 정의 변수로 취급됩니다.
$ BASHPID=$(echo $$) $ echo $$;echo $BASHPID
이 경우 BASHPID의 특수 변수 기능을 되찾으려면 새로운 터미널 세션을 열어야 합니다.
마무리
지금까지 $$와 BASHPID의 차이점과 각각의 동작 방식을 예제와 함께 살펴보았습니다. 두 변수는 메인 셸에서는 같은 값을 반환하지만, 서브셸 환경에서는 전혀 다른 결과를 보인다는 점이 핵심입니다. 직접 실습해 보시고 소중한 피드백을 공유해 주시기 바랍니다.