리눅스나 유닉스 계열 환경에서 쉘을 쓰다 보면 명령어 맨 뒤에 &를 붙인 예제를 자주 보게 된다. 처음 보면 그냥 “뒤에 뭔가 붙이는 기호” 정도로 보이지만, 실제로는 쉘의 실행 방식과 작업 제어(job control)를 이해하는 데 매우 중요한 문법이다. 결론부터 말하면, 쉘에서 &해당 명령을 포그라운드가 아니라 백그라운드에서 실행하라는 뜻이다.

보통 터미널에서 명령어를 실행하면, 그 명령이 끝날 때까지 현재 쉘은 그 작업을 붙잡고 있게 된다. 예를 들어 sleep 10을 실행하면 10초 동안 터미널은 그 작업이 끝나기를 기다린다. 이 상태에서는 사용자가 다음 명령을 바로 입력하기 어렵다. 이런 실행 방식을 포그라운드 실행(foreground execution)이라고 한다.

sleep 10

반면에 같은 명령 뒤에 &를 붙이면 상황이 달라진다.

sleep 10 &

이제 sleep은 백그라운드에서 실행되고, 쉘은 즉시 프롬프트를 다시 보여준다. 즉 사용자는 sleep이 끝나기를 기다리지 않고 곧바로 다른 명령을 입력할 수 있다. 이것이 &의 가장 기본적인 의미다.

&를 붙이면 실제로 무슨 일이 일어날까

쉘은 command &를 만나면 그 명령을 비동기적으로 실행한다. 쉽게 말해, 현재 쉘 세션 안에서 새로운 작업으로 등록해 두고 사용자에게 제어권을 바로 돌려준다. 그래서 긴 작업을 돌려놓고도 터미널을 계속 쓸 수 있다.

예를 들어 다음처럼 실행해 보자.

sleep 100 &

그러면 보통 아래와 비슷한 출력이 나타난다.

[1] 12345

여기서 [1]은 **잡 번호(job number)**이고, 12345는 **프로세스 ID(PID)**다. 잡 번호는 현재 쉘이 관리하는 작업의 번호이고, PID는 운영체제 수준에서 해당 프로세스를 식별하는 번호다. 이 둘은 비슷해 보이지만 같은 것은 아니다. 쉘은 잡 번호를 기준으로 작업을 다루고, 운영체제는 PID를 기준으로 프로세스를 관리한다.

왜 백그라운드 실행이 필요한가

가장 단순한 이유는 시간이 오래 걸리는 명령을 실행하면서도 터미널을 계속 쓰고 싶기 때문이다. 예를 들어 대용량 압축, 파일 복사, 서버 실행, 로그 분석, 테스트 실행 등은 몇 초에서 몇 분, 때로는 더 오래 걸릴 수 있다. 이런 작업을 매번 포그라운드로 실행하면 터미널이 계속 묶인다.

예를 들어 웹 서버를 테스트로 띄운다고 해보자.

python -m http.server 8000 &

이렇게 하면 서버는 백그라운드에서 떠 있고, 사용자는 같은 터미널에서 곧바로 curl, ps, jobs 같은 다른 명령을 계속 실행할 수 있다.

또 다른 예로 로그 분석 명령이 오래 걸리는 경우가 있다.

grep "ERROR" large.log > error.txt &

이 명령은 백그라운드에서 돌아가며 결과를 error.txt에 저장한다. 사용자는 그동안 다른 점검 작업을 할 수 있다.

포그라운드와 백그라운드의 차이

핵심 차이는 쉘이 기다리느냐, 기다리지 않느냐다.

포그라운드 실행에서는 쉘이 해당 명령이 끝날 때까지 대기한다.

find / -name "*.log"

반대로 백그라운드 실행에서는 쉘이 기다리지 않고 다음 입력을 받는다.

find / -name "*.log" > logs.txt 2>/dev/null &

하지만 여기서 주의할 점이 있다. 백그라운드라고 해서 “완전히 독립된 다른 세계”로 가는 것은 아니다. 여전히 현재 쉘 세션과 연결되어 있고, stdout/stderr 처리 방식에 따라 터미널 화면에 출력을 뿌릴 수도 있다. 그래서 실무에서는 &만 단독으로 쓰기보다 리다이렉션과 함께 쓰는 경우가 많다.

출력이 왜 섞이는가

백그라운드 작업도 기본적으로는 표준출력(stdout)과 표준에러(stderr)를 현재 터미널로 보낸다. 그래서 아래처럼 실행하면:

python app.py &

백그라운드에서 실행되는 앱의 로그가 계속 현재 터미널에 찍힐 수 있다. 그러면 사용자가 입력하는 명령과 출력이 뒤섞여 보기 불편해진다. 이 문제를 막기 위해 보통 출력 리다이렉션을 함께 사용한다.

python app.py > app.log 2>&1 &

이 명령은 표준출력과 표준에러를 모두 app.log 파일로 보낸 뒤, 프로세스를 백그라운드에서 실행한다. 실제 서버 실행 예제에서 자주 보는 형태다.

출력을 버리고 싶다면 다음처럼 쓸 수도 있다.

python app.py > /dev/null 2>&1 &

이 경우 화면에는 아무 출력도 남지 않는다.

jobs, fg, bg와의 관계

&를 이해했다면 이어서 알아야 할 것이 작업 제어 명령이다. 현재 쉘이 관리 중인 백그라운드 작업은 jobs로 확인할 수 있다.

jobs

예를 들어 결과가 다음처럼 나올 수 있다.

[1]+  Running                 sleep 100 &

이 작업을 다시 포그라운드로 가져오고 싶다면 fg를 쓴다.

fg %1

여기서 %1은 1번 잡을 뜻한다. 반대로 포그라운드에서 실행 중인 작업을 Ctrl+Z로 일시정지한 뒤 다시 백그라운드로 보내려면 bg를 쓴다.

bg %1

&처음부터 백그라운드로 시작하는 방법이고, bg멈춘 작업을 뒤로 보내는 방법이라고 보면 된다.

&&&는 완전히 다르다

초보자가 자주 헷갈리는 부분 중 하나가 &&&다. 둘은 비슷하게 생겼지만 의미가 완전히 다르다.

&는 백그라운드 실행이다.

sleep 5 &
echo "next"

위 경우 sleep 5는 뒤에서 돌고, echo "next"는 바로 실행된다.

반면 &&는 앞 명령이 성공했을 때만 뒤 명령을 실행한다.

mkdir test && cd test

여기서는 mkdir test가 성공해야만 cd test가 실행된다. 즉 &는 작업 제어이고, &&는 조건부 명령 연결이다. 이 둘을 섞어 이해하면 안 된다.

&만으로 충분하지 않은 경우

백그라운드 실행은 편리하지만, 터미널을 닫았을 때도 계속 살아 있어야 하는 작업이라면 &만으로는 부족할 수 있다. 많은 경우 프로세스가 세션 종료의 영향을 받기 때문이다. 그래서 장시간 유지할 프로세스는 nohup과 함께 쓰는 경우가 많다.

nohup python app.py > app.log 2>&1 &

이 형태는 서버, 배치, 스크립트 실행에서 매우 흔하다. nohup은 터미널 종료 신호의 영향을 덜 받게 하고, &는 백그라운드 실행을 맡는다. 역할이 다르다. &는 “뒤에서 실행”, nohup은 “세션 종료 후에도 버티기”에 가깝다.

실전에서 자주 보는 예제

간단한 백업 스크립트를 백그라운드로 돌리는 경우:

tar -czf backup.tar.gz /home/user/data > backup.log 2>&1 &

자바 애플리케이션 실행:

java -jar app.jar > app.log 2>&1 &

쉘 스크립트 실행:

./batch.sh > batch.log 2>&1 &

여러 작업을 병렬로 실행:

./job1.sh &
./job2.sh &
./job3.sh &
wait

여기서 wait는 현재 쉘이 시작한 백그라운드 작업들이 끝날 때까지 기다리는 명령이다. 여러 작업을 동시에 돌리고 마지막에 한 번 합류하고 싶을 때 유용하다.

정리

쉘에서 &는 단순한 기호가 아니라, 명령을 백그라운드에서 실행하도록 만드는 핵심 문법이다. 이것을 이해하면 왜 어떤 명령은 터미널을 붙잡고 있고, 어떤 명령은 뒤에서 돌면서 프롬프트를 바로 반환하는지 자연스럽게 보이기 시작한다. 또 jobs, fg, bg, nohup, 리다이렉션 같은 주제도 함께 연결된다.

실무에서 &는 매우 자주 쓰인다. 다만 그냥 “뒤에 붙이는 것” 정도로 외우면 금방 한계가 온다. 백그라운드 실행, 출력 처리, 세션 종료, 작업 제어를 한 묶음으로 이해해야 제대로 쓸 수 있다. 결국 &는 짧은 기호 하나지만, 그 뒤에는 쉘의 실행 모델 전체가 들어 있다.