쉘 내부 명령어와 외부 명령어
- CS
- 운영체제
정의 및 특징
내부 명령어 (Built-in Commands)
쉘 프로그램 (/bin/bash, /bin/zsh, /bin/sh 등) 내부에 함수나 로직으로 이미 구현되어 있는 명령어다.
특징
- 쉘 실행 파일의 코드 일부로 포함됨
- 쉘이 시작될 때 메모리 (RAM) 에 함께 로드됨
- 별도의 실행 파일로 존재하지 않음
- 현재 쉘 프로세스 내부에서 직접 실행
- 새로운 프로세스 생성 없음 (fork/exec 불필요)
- PATH 환경 변수와 무관하게 항상 사용 가능
예시
cd # 디렉터리 변경
export # 환경 변수 설정
alias # 별칭 설정
history # 명령어 이력 조회
set # 쉘 옵션 설정
source (.) # 스크립트 실행
exit # 쉘 종료
echo # 텍스트 출력 (bash 내장)
pwd # 현재 디렉터리 표시 (bash 내장)외부 명령어 (External Commands)
별도의 실행 파일 (바이너리 또는 스크립트) 로 디스크에 저장되어 있는 명령어다.
특징
- 독립적인 실행 파일로 존재 (
/bin,/usr/bin,/usr/local/bin등) - 실행 시 디스크에서 읽어와야 함
- 새로운 프로세스를 생성하여 실행 (fork + exec)
- PATH 환경 변수에서 파일 위치를 검색
- 별도의 메모리 공간에서 실행
예시
ls # 디렉터리 내용 표시 (/bin/ls)
cat # 파일 내용 출력 (/bin/cat)
grep # 텍스트 검색 (/bin/grep)
find # 파일 검색 (/usr/bin/find)
gcc # C 컴파일러 (/usr/bin/gcc)
python # Python 인터프리터 (/usr/bin/python)PATH 란?
PATH는 쉘이 외부 명령어(실행 파일)를 찾을 때 검색하는 디렉토리 목록을 담고 있는 환경 변수다.
# PATH 내용 확인
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games콜론(:)으로 여러 디렉터리 경로를 구분하고, 왼쪽부터 오른쪽 순서로 검색한다.
PATH=/첫번째/디렉터리:/두번째/디렉터리:/세번째/디렉터리실행 방식의 차이
내부 명령어 실행 과정

코드 예시 (의사 코드)
// bash 내부 구조 (단순화)
int main() {
while (1) {
char *command = read_command();
if (strcmp(command, "cd") == 0) {
// 내부 명령어 - 직접 실행
builtin_cd(args); // 함수 호출
} else if (strcmp(command, "export") == 0) {
builtin_export(args);
} else {
// 외부 명령어 - fork/exec
execute_external(command);
}
}
}
void builtin_cd(char *path) {
if (chdir(path) != 0) {
perror("cd failed");
}
// PWD 환경 변수 업데이트
update_pwd();
}외부 명령어 실행 과정

코드 예시 (의사 코드)
void execute_external(char *command, char **args) {
// PATH에서 실행 파일 찾기
char *path = find_in_path(command); // "/bin/ls"
pid_t pid = fork(); // 프로세스 복제
if (pid == 0) {
// 자식 프로세스
execve(path, args, env); // 프로그램 실행
perror("exec failed");
exit(1);
} else {
// 부모 프로세스 (쉘)
int status;
waitpid(pid, &status, 0); // 자식 종료 대기
}
}왜 내부 명령어와 외부 명령어를 구분할까?
쉘 상태 변경 필요성
일부 명령어는 반드시 현재 쉘 프로세스의 상태를 변경해야 한다.
cd (디렉터리 변경)
내부 명령어로 구현해야 하는 이유
# cd가 만약 외부 명령어라면?
$ pwd
/home/user
$ cd /tmp # 외부 프로세스에서 디렉터리 변경
$ pwd
/home/user # 변경되지 않음!왜 그럴까?
- 외부 명령어는 자식 프로세스에서 실행
- 자식 프로세스가
chdir()를 호출해도 자식의 디렉터리만 변경 - 부모 (쉘) 의 현재 디렉터리는 그대로
- 자식이 종료되면 변경 사항도 사라짐
내부 명령어로 구현하면
$ pwd
/home/user
$ cd /tmp # 쉘 프로세스 자체에서 디렉터리 변경
$ pwd
/tmp # 변경됨!export (환경 변수 설정)
내부 명령어로 구현해야 하는 이유
# export가 만약 외부 명령어라면?
$ echo $MY_VAR
(비어있음)
$ export MY_VAR=hello # 외부 프로세스에서 환경 변수 설정
$ echo $MY_VAR
(여전히 비어있음)왜 그럴까?
- 환경 변수는 각 프로세스마다 독립적
- 자식 프로세스가 환경 변수를 설정해도 부모에게 영향 없음
- 자식이 종료되면 환경 변수도 사라짐
내부 명령어로 구현하면
$ echo $MY_VAR
(비어있음)
$ export MY_VAR=hello # 쉘 자체의 환경 변수 설정
$ echo $MY_VAR
hello # 설정됨!속도와 효율성
외부 명령어 실행 비용
1. fork() 시스템 콜: 프로세스 복제
2. PATH 검색: 디스크에서 파일 찾기
3. 파일 읽기: 디스크 I/O
4. 메모리 로드: 프로그램을 RAM에 적재
5. exec() 시스템 콜: 프로그램 실행
6. exit() 및 wait(): 종료 처리차이가 나는 이유
- 내부 명령어: 메모리에서 직접 실행
- 외부 명령어: fork, 디스크 읽기, exec 등의 오버헤드
RAM 에 상주시키는 이유
디스크 vs RAM 속도 차이
접근 속도 비교
RAM 접근 시간: ~100 나노초 (0.0001 밀리초)
SSD 접근 시간: ~0.1 밀리초
HDD 접근 시간: ~10 밀리초
RAM이 SSD보다 1,000배 빠름
RAM이 HDD보다 100,000배 빠름매번 디스크에서 읽는 경우의 문제
외부 명령어 실행 과정 (디스크 읽기 포함)
# 사용자가 ls를 100번 실행한다면?
$ ls # 1번: 디스크에서 /bin/ls 읽기 (10ms)
$ ls # 2번: 디스크에서 /bin/ls 읽기 (10ms)
$ ls # 3번: 디스크에서 /bin/ls 읽기 (10ms)
...
$ ls # 100번: 디스크에서 /bin/ls 읽기 (10ms)
# 총 1000ms (1초) 소요문제점
- 매번 디스크 I/O 발생 → 느림
- 디스크 헤드 이동 시간 소요 (HDD 의 경우)
- 자주 사용하는 명령어일수록 비효율적
내부 명령어로 RAM 에 상주시키는 경우
쉘 시작 시 한 번만 로드
부팅/쉘 시작 시:
1. /bin/bash 실행 파일을 디스크에서 읽기
2. bash 코드를 RAM에 로드
3. 내부 명령어 함수들도 함께 RAM에 로드
이후 명령어 실행 시:
1. RAM에서 직접 함수 호출 (~0.0001ms)
2. 디스크 접근 불필요장점
# 사용자가 cd를 100번 실행한다면?
$ cd /home # 1번: RAM에서 직접 실행 (0.0001ms)
$ cd /tmp # 2번: RAM에서 직접 실행 (0.0001ms)
$ cd /var # 3번: RAM에서 직접 실행 (0.0001ms)
...
# 총 0.01ms (거의 즉시)RAM 사용의 트레이드오프
장점
- 속도: 디스크 접근 없이 즉시 실행
- 효율성: 프로세스 생성 오버헤드 없음
- 응답성: 사용자 경험 향상
단점
- 메모리 사용: 쉘이 항상 RAM 공간 차지
- 유연성 제한: 쉘을 재시작하지 않고는 업데이트 불가
균형점
자주 사용하는 명령어 → 내부 명령어 (RAM 상주)
- cd, export, alias, echo, pwd 등
- 총 메모리 사용: 매우 작음 (수백 KB)
가끔 사용하는 명령어 → 외부 명령어 (디스크 저장)
- find, gcc, ffmpeg 등
- 필요할 때만 로드 → RAM 절약페이지 캐시를 통한 최적화
실제로 외부 명령어도 자주 사용하면 빨라진다.
OS 의 페이지 캐시
첫 실행:
$ ls # 디스크에서 읽기 (10ms)
# OS가 /bin/ls를 페이지 캐시에 저장
이후 실행:
$ ls # 페이지 캐시에서 읽기 (0.1ms)
$ ls # 페이지 캐시에서 읽기 (0.1ms)메커니즘
- 외부 명령어를 처음 실행할 때 디스크에서 읽음
- OS 가 자동으로 RAM 의 페이지 캐시에 저장
- 이후 실행 시 캐시에서 읽어옴 (디스크 접근 불필요)
- 메모리가 부족하면 자동으로 캐시 제거
장점
- 자주 사용하는 외부 명령어도 빠르게 실행
- 명시적 관리 불필요
- 메모리 효율적 (자동 관리)