스크립트에서 cd /tmp를 실행했는데, 스크립트가 끝난 뒤 터미널은 여전히 원래 폴더에 있다. cd가 고장 난 걸까? 아니다. 어느 셸에서 폴더를 바꿨는지가 핵심이다.
cd와 ls는 Bash에서 어떻게 실행될까?
cd는 현재 셸이 직접 실행하는 내장 명령이다. 현재 작업 디렉터리는 그 셸의 상태이므로, 같은 셸이 cd를 실행해야 이후 pwd도 바뀐다. 반면 ls는 보통 파일로 존재하는 실행 프로그램을 Bash가 찾아 실행한다. ls가 파일 목록을 출력해도 부모 셸의 작업 디렉터리가 바뀌지 않는 이유다.
ls의 실제 경로와 cd의 해석 방식은 현재 셸의 별칭·함수·환경에 따라 달라질 수 있다. 추측하기보다 지금 쓰는 Bash에 물어보자.
type -t로 내장 명령과 실행 파일을 어떻게 구분할까?
아래는 별칭 설정을 읽지 않은 Bash에서 확인한 결과다. type은 그 셸이 이름을 어떻게 해석하는지 보여 준다.
bash --noprofile --norc -c 'type -t cd; type -t ls; compgen -b | grep -x cd'builtin
file
cdbuiltin은 Bash 안의 명령, file은 찾아 실행할 파일이다. compgen -b도 Bash 내장 명령 목록을 보여 주므로 cd가 포함된다. 파일의 실제 위치까지 보려면 type -a ls를 쓴다. /bin/ls인지 /usr/bin/ls인지는 환경마다 다르다.
자식 셸에서 cd했는데 원래 pwd가 그대로인 이유는?
이번에는 같은 /tmp에서 출발해 자식 셸만 /로 이동시킨다. 이어 원래 셸에서 직접 cd해 결과를 비교한다.
cd /tmp
pwd
bash --noprofile --norc -c 'cd /; pwd'
pwd
cd /
pwd/tmp
/
/tmp
/가운데 /는 자식 셸의 pwd다. 그 다음 /tmp는 원래 셸의 pwd이므로 그대로다. 마지막에 원래 셸이 직접 cd /를 실행하고 나서야 그 셸의 위치가 /가 된다. 자식이 부모의 작업 디렉터리를 거꾸로 바꿀 수는 없다.
이 차이는 배포 스크립트에서도 자주 만난다. bash setup.sh 안의 cd는 setup.sh가 실행되는 셸에만 적용된다. 이후 터미널까지 그 위치로 옮기고 싶다면 터미널에서 직접 cd해야 한다. 반대로 스크립트 안에서만 경로를 바꿔 다음 명령을 실행하려면 cd /tmp && ./run.sh처럼 같은 셸에서 이어 쓰면 된다. cd가 실패했을 때 다음 명령이 엉뚱한 폴더에서 실행되지 않게 &&를 두는 편이 안전하다.
which cd에 경로가 나와도 내장 명령일까?
그럴 수 있다. which는 실행 파일을 찾는 도구로 쓰이지만, 시스템에 같은 이름의 보조 실행 파일이 있거나 셸마다 which 자체의 구현이 다를 수 있다. 경로가 보인다는 사실만으로 Bash가 그 파일을 실행했다고 단정하지 말자. 실제로 이 글의 독립 실행 환경에서는 type cd가 내장 명령을 가리키면서도 which cd는 /usr/bin/cd를 출력했다.
지금 입력한 명령의 정체가 궁금하면 type -a cd로 후보를 확인하고, 현재 Bash가 우선 해석하는 종류는 type -t cd로 확인하면 된다. 별칭이나 함수가 끼어 있다면 그 사실도 드러난다. which 출력 하나로 결론을 내리는 것보다 덜 헷갈린다.
핵심 요약
cd는 현재 Bash의 작업 디렉터리를 바꾸는 내장 명령이다. 별도 셸에서 실행한 cd는 그 셸만 이동시킨다. type -t로 지금 셸이 명령을 어떻게 해석하는지 확인하고, 경로만 찾는 which 결과와 구분해서 읽자.

