포그라운드와 백그라운드
명령어를 실행하면 기본적으로 터미널이 멈춥니다. 명령어가 끝날 때까지 다른 것을 입력할 수 없습니다. 이것이 포그라운드 실행입니다. 짧은 명령어라면 문제없지만, 몇 분씩 걸리는 작업이라면 터미널 하나가 통째로 묶이게 됩니다.
백그라운드 실행은 이 문제를 해결합니다. 작업을 뒤에서 돌리면서 터미널은 즉시 다음 명령을 받을 준비를 합니다. 백업, 로그 분석, 데이터 변환처럼 시간이 걸리는 작업을 스크립트로 자동화할 때 자주 사용하는 패턴입니다.
포그라운드 실행
별다른 설정 없이 명령어를 입력하면 포그라운드에서 실행됩니다.
# 포그라운드 실행 (5초 동안 프롬프트가 돌아오지 않음)sleep 5# 종료되면 프롬프트 복귀echo "완료"
포그라운드 실행 중에는 Ctrl+C로 프로세스를 강제 종료하거나, Ctrl+Z로 일시 정지할 수 있습니다.
백그라운드 실행: & 기호
명령어 끝에 &를 붙이면 백그라운드에서 실행됩니다.
# 백그라운드 실행sleep 30 &# 즉시 프롬프트 복귀. [1]은 작업 번호, 12345는 PID# [1] 12345# 바로 다음 명령어 입력 가능echo "백그라운드로 보냈습니다."
&로 백그라운드 실행을 하면 쉘이 작업 번호와 PID를 출력합니다. 작업 번호(1, 2, ...)는 현재 쉘 세션 안에서의 번호이고, PID는 시스템 전체에서 고유한 번호입니다.
여러 작업을 동시에 백그라운드로 실행할 수도 있습니다.
# 세 가지 작업을 동시에 시작./backup.sh &./log_rotate.sh &./cleanup.sh &echo "세 작업을 백그라운드로 시작했습니다."
Ctrl+Z로 현재 작업 일시 정지
실행 중인 포그라운드 프로세스에 Ctrl+Z를 누르면 일시 정지됩니다.
# 포그라운드에서 실행 중sleep 100# Ctrl+Z 입력 →# [1]+ Stopped sleep 100
정지된 프로세스는 메모리에 남아있지만 CPU를 사용하지 않습니다. bg 명령으로 백그라운드에서 재개하거나, fg 명령으로 다시 포그라운드로 가져올 수 있습니다.
서브쉘과 명령어 그룹
여러 명령어를 묶어서 실행하는 두 가지 방법이 있습니다.
소괄호: 서브쉘
(commands) 형식은 새로운 서브쉘을 만들어 그 안에서 명령어를 실행합니다. 서브쉘 안에서 디렉토리를 변경하거나 변수를 설정해도 부모 쉘에는 영향을 미치지 않습니다.
# 서브쉘: 내부 cd가 부모에 영향 없음( cd /tmp echo "현재 위치: $(pwd)" # /tmp) &echo "부모 위치: $(pwd)" # 원래 위치 그대로
중괄호: 현재 쉘 그룹
{ commands; } 형식은 현재 쉘에서 명령어를 그룹으로 실행합니다. 중괄호 안의 명령어는 부모 쉘의 변수와 디렉토리를 공유합니다. 여는 중괄호 뒤에 공백이 필요하고, 마지막 명령어 뒤에 세미콜론이 필요합니다.
# 현재 쉘 그룹: 내부 변수가 부모에 영향{ LOG_DIR="/var/log/myapp" echo "로그 디렉토리: $LOG_DIR" echo "설정 완료"} > setup.logecho "$LOG_DIR" # 변수 사용 가능
백그라운드로 여러 명령어를 묶어 실행할 때는 서브쉘을 주로 사용합니다.
# 여러 작업을 하나의 백그라운드 그룹으로( echo "작업 1 시작" sleep 2 echo "작업 1 완료") &( echo "작업 2 시작" sleep 3 echo "작업 2 완료") &echo "두 그룹을 백그라운드로 실행했습니다."
wait 명령어로 백그라운드 완료 대기
백그라운드 프로세스들이 모두 끝날 때까지 기다려야 할 때는 wait를 사용합니다.
#!/usr/bin/env bash# 파일: parallel_demo.shecho "병렬 처리 시작: $(date)"# 세 가지 작업을 동시에 시작(sleep 3 && echo "작업 A 완료") &PID_A=$!(sleep 1 && echo "작업 B 완료") &PID_B=$!(sleep 2 && echo "작업 C 완료") &PID_C=$!echo "세 작업 실행 중..."# 특정 PID 대기wait $PID_Becho "B가 먼저 끝났습니다."# 모든 백그라운드 작업 대기waitecho "모든 작업 완료: $(date)"
$!는 마지막으로 백그라운드에서 실행된 프로세스의 PID를 담고 있는 특수 변수입니다. 각 백그라운드 작업을 시작한 직후 $!를 변수에 저장해두면 나중에 개별 PID로 대기하거나 종료할 수 있습니다.
실행 결과는 다음과 같습니다.
병렬 처리 시작: Thu Apr 24 10:35:00 KST 2026
세 작업 실행 중...
작업 B 완료
B가 먼저 끝났습니다.
작업 C 완료
작업 A 완료
모든 작업 완료: Thu Apr 24 10:35:03 KST 2026
세 작업이 순서대로 실행됐다면 6초(3+1+2)가 걸렸겠지만, 병렬로 실행했기 때문에 가장 긴 작업(3초)만큼만 걸렸습니다.
실습: 여러 작업을 백그라운드로 실행하고 결과 취합
여러 디렉토리의 파일 통계를 병렬로 수집하고 결과를 하나로 합치는 스크립트입니다.
#!/usr/bin/env bash# 파일: parallel_stats.shset -euo pipefailRESULT_DIR=$(mktemp -d)DIRS=("/usr/bin" "/usr/lib" "/etc")echo "디렉토리별 파일 통계 수집 시작"# 각 디렉토리를 백그라운드로 분석pids=()for dir in "${DIRS[@]}"; do ( count=$(find "$dir" -maxdepth 1 -type f 2>/dev/null | wc -l) size=$(du -sh "$dir" 2>/dev/null | cut -f1) echo "$dir: 파일 ${count}개, 크기 ${size}" ) > "${RESULT_DIR}/$(basename "$dir").txt" & pids+=($!)done# 모든 작업 완료 대기for pid in "${pids[@]}"; do wait "$pid"doneecho ""echo "===== 통계 결과 ====="cat "${RESULT_DIR}"/*.txt# 임시 디렉토리 정리rm -rf "$RESULT_DIR"echo ""echo "완료"
실행 결과입니다.
디렉토리별 파일 통계 수집 시작
===== 통계 결과 =====
/etc: 파일 203개, 크기 3.2M
/usr/bin: 파일 846개, 크기 178M
/usr/lib: 파일 1243개, 크기 2.1G
완료
배열에 PID를 담아두고 나중에 하나씩 wait하는 패턴은 병렬 처리 스크립트에서 매우 흔하게 사용됩니다. 이 패턴을 익혀두면 순차 처리보다 훨씬 빠른 스크립트를 작성할 수 있습니다.