일반적인 Bash 문법 오류 해결: 실용 가이드
따옴표, 대괄호, 변수, 리다이렉션 및 명령어 검색 문제에 대한 예제를 통해 일반적인 Bash 문법 오류를 수정합니다.
일반적인 Bash 문법 오류 해결: 실용 가이드
Bash 문법 오류는 대개 작은 실수에서 비롯됩니다: 누락된 따옴표, 잘못된 대괄호, 잘못 배치된 리다이렉션, 또는 예상치 못한 값으로 확장된 변수 등입니다. Bash 스크립트가 syntax error near unexpected token 오류와 함께 중단되면 Bash가 보고한 줄의 이전 줄을 먼저 확인한 후 스크립트를 문법 검사(syntax check)로 실행해 보세요.
서버에서 변경된 스크립트를 실행하기 전에 다음 빠른 검사를 사용하세요:
bash -n script.sh
bash -n은 명령어를 실행하지 않고 파일을 구문 분석합니다. 모든 논리 버그를 잡아내지는 못하지만, 깨진 따옴표, 누락된 fi 문, 잘못된 루프 등을 많이 잡아냅니다.
누락된 따옴표
닫히지 않은 따옴표는 파서를 혼란스럽게 하는 가장 빠른 방법 중 하나입니다.
name="deploy
printf 'Deploying %s\n' "$name"
Bash는 닫는 "를 계속 찾기 때문에 이후 줄을 계속 읽습니다. 따옴표를 닫고 공백이 포함될 수 있는 변수 확장을 따옴표로 묶어 해결하세요:
name="deploy"
printf 'Deploying %s\n' "$name"
변수를 확장하려면 큰따옴표를 사용하세요. 리터럴 텍스트를 원할 때는 작은따옴표를 사용하세요:
printf 'HOME stays literal: $HOME\n'
printf "HOME expands: %s\n" "$HOME"
잘못된 if, for, while 블록
모든 복합 명령어는 닫는 키워드가 필요합니다. 하나가 누락되면 종종 파일 끝 근처에서 오류가 보고됩니다.
if systemctl is-active --quiet nginx; then
echo "nginx is running"
# missing fi
올바른 버전:
if systemctl is-active --quiet nginx; then
echo "nginx is running"
fi
루프와 case 문에도 동일한 패턴이 적용됩니다:
for host in web1 web2 web3; do
ssh "$host" uptime
done
case "$env" in
prod) echo "production" ;;
dev) echo "development" ;;
*) echo "unknown" ;;
esac
대괄호 및 테스트 실수
[ 명령어는 실제 명령어이므로 인수 주위와 닫는 대괄호 앞에 공백이 필요합니다.
잘못된 예:
if [$count -gt 5]; then
echo "too many"
fi
수정된 예:
if [ "$count" -gt 5 ]; then
echo "too many"
fi
Bash 전용 스크립트의 경우, [[ ... ]]는 빈 변수를 더 우아하게 처리하고 패턴 매칭을 지원하므로 문자열 테스트에 더 안전합니다:
if [[ "$file" == *.log ]]; then
gzip "$file"
fi
숫자에는 숫자 연산자를, 텍스트에는 문자열 연산자를 사용하세요:
[[ "$status" == "ready" ]] # 문자열 비교
[[ "$retries" -lt 3 ]] # 숫자 비교
변수 확장 문제
일반적인 실수는 할당 시 = 주위에 공백을 추가하는 것입니다. Bash는 이를 변수 할당이 아닌 명령어로 처리합니다.
잘못된 예:
backup_dir = /var/backups
수정된 예:
backup_dir=/var/backups
변수 이름이 다른 텍스트와 접촉할 때는 중괄호를 사용하세요:
service="nginx"
log="/var/log/${service}.log"
중괄호가 없으면 Bash가 의도한 것보다 더 긴 변수 이름을 읽을 수 있습니다.
command not found 오류
command not found는 항상 문법 오류는 아닙니다. 일반적으로 Bash가 해당 이름의 실행 파일을 찾을 수 없음을 의미합니다.
먼저 오타를 확인하세요:
systemctl status nginx
그런 다음 명령어가 존재하고 PATH에 있는지 확인하세요:
command -v systemctl
printf '%s\n' "$PATH"
스크립트가 cron, systemd 또는 CI 작업에서 실행되는 경우 PATH가 대화형 셸보다 짧을 수 있습니다. 중요한 명령어에는 절대 경로를 사용하거나 스크립트 상단 근처에 PATH를 설정하세요:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export PATH
리다이렉션 및 파이프 오류
리다이렉션 순서가 중요합니다. 이 명령어는 stdout을 app.log에 쓰고 stderr를 같은 위치로 보냅니다:
./deploy.sh >app.log 2>&1
이 명령어는 stdout이 리다이렉션되기 전에 stderr가 이전 stdout에 복사되므로 다릅니다:
./deploy.sh 2>&1 >app.log
파이프라인의 경우 Bash는 일반적으로 마지막 명령어의 종료 코드를 반환합니다. 중간에 실패한 명령어가 전체 파이프라인을 실패하게 하려면 pipefail을 사용하세요:
set -o pipefail
kubectl get pods | grep CrashLoopBackOff
실용적인 디버깅 흐름
문법 검사로 시작하세요:
bash -n script.sh
문법이 유효하지만 동작이 잘못된 경우 추적(tracing)으로 실행하세요:
bash -x script.sh
더 안전한 프로덕션 스크립트를 위해 의도적으로 엄격 모드를 추가하고 각 변경 후 스크립트를 테스트하세요:
set -euo pipefail
set -e는 많은 명령어 실패 시 종료하고, set -u는 설정되지 않은 변수를 오류로 처리하며, pipefail은 파이프라인 내부의 실패를 잡아냅니다. 이러한 옵션은 유용하지만 스크립트 동작을 변경할 수 있으므로 테스트 없이 기존 스크립트에 무분별하게 추가하지 말고 의도적으로 활성화하세요.
핵심 요약
대부분의 Bash 문법 오류는 따옴표, 닫는 키워드, 대괄호 공백, 변수 할당 및 리다이렉션을 순서대로 확인하면 간단해집니다. bash -n과 bash -x를 일반 워크플로에 포함시키고, 프로덕션에서 실행될 동일한 셸과 환경으로 스크립트를 테스트하세요.