명령을 묶는 두 방식
{ cmd1; cmd2; }: 현재 셸에서 실행하므로 변수, 옵션과 디렉터리 변경이 뒤에도 남아.(cmd1; cmd2): 별도 서브셸 환경에서 실행하므로 그 안의 셸 상태 변경이 부모 셸로 돌아오지 않아.
둘 다 여러 명령을 하나의 복합 명령으로 다룰 수 있지만 상태가 남는 범위가 달라. 외부 파일과 서비스에 만든 부수 효과는 서브셸을 썼다고 자동으로 되돌아가지 않아.
중괄호는 현재 셸에서 함께 처리해
여러 명령의 출력을 하나의 파일로 보낼 때 유용해.
{
echo "=== build log ==="
date
./build.sh
} > build.log 2>&1블록 전체에 리디렉션이 한 번 적용돼. 현재 셸에서 실행되므로 블록 안의 cd와 변수 할당은 뒤 명령에도 영향을 줄 수 있어.
괄호는 셸 상태를 격리해
(
cd ~/projects/temp || exit
./make.sh
)
# 부모 셸의 현재 디렉터리는 바뀌지 않아작업 디렉터리나 셸 옵션을 잠깐 바꾸고 끝낼 때 편해. 다만 그 안에서 수정한 파일, 보낸 네트워크 요청과 시작한 외부 프로세스까지 취소되는 트랜잭션은 아니야.
명령 치환도 별도 실행 환경을 사용해
result=$(cmd)는 명령의 stdout을 값으로 캡처해. 명령 치환 안에서 바꾼 셸 변수와 현재 디렉터리는 부모 셸에 남지 않지만, stderr는 별도로 리디렉션하지 않으면 바깥으로 보일 수 있어. 큰 데이터나 NUL 바이트를 셸 변수에 담는 방식은 적합하지 않으므로 출력 형식과 크기를 확인해.
중괄호 문법에는 명령 경계가 필요해
{와 }는 예약어라 주변 공백이 필요하고, 닫는 중괄호 앞에는 세미콜론이나 줄바꿈으로 앞 명령을 끝내야 해. { cmd; }처럼 쓰면 돼. 괄호와 중괄호를 한 줄로 지나치게 압축하면 리디렉션 범위와 실패 지점을 읽기 어려워지므로 여러 단계는 블록 모양을 유지해.
서브셸은 실패 처리를 대신하지 않아
서브셸 안에서 첫 명령이 실패해도 다음 명령이 계속 실행될지는 &&, if, 셸 옵션과 명시적인 종료 처리에 달렸어. 디렉터리 변경이 부모에 남지 않는다는 사실과 작업 전체가 원자적으로 성공한다는 주장은 전혀 달라. 실패 뒤 남을 파일과 정리 절차도 따로 설계해야 해.
그룹의 상태는 마지막 명령만 보면 놓칠 수 있어
중괄호 그룹과 서브셸 그룹은 기본적으로 마지막에 실행한 명령의 종료 상태를 바깥에 돌려줘. 앞 단계가 실패하고 마지막 echo가 성공하면 그룹이 성공처럼 보일 수 있어. 여러 단계를 하나의 게이트로 사용할 때는 실패 시 즉시 0이 아닌 상태를 반환하게 만들고, 결과 파일은 모든 단계가 끝난 뒤에만 교체해.