위치 매개변수 마스터하기: Bash 스크립트 인자 가이드
위치 매개변수를 마스터하여 동적 Bash 스크립트의 잠재력을 활용하세요. 이 포괄적인 가이드는 `$1`, `$2`와 같은 명령줄 인자에 접근하는 방법과 `$#`(인자 개수) 및 중요한 `"$@"`(모든 인자)와 같은 특수 변수를 설명합니다. 입력 검증을 위한 필수 모범 사례를 배우고, `$*`와 `$@`의 차이점을 이해하며, 사용자 입력에 완벽하게 적응하는 강력하고 오류 검사된 스크립트를 작성하기 위한 실용적인 예제를 확인하세요.
위치 매개변수 마스터하기: Bash 스크립트 인자 가이드
Bash 스크립트는 파일 내부의 변수를 직접 수정하는 대신 인자를 받아들이면 훨씬 더 유용해집니다. 백업 스크립트는 소스 디렉터리를 받아야 합니다. 배포 스크립트는 환경 이름을 받아야 합니다. 정리 스크립트는 하나 이상의 경로를 받아야 합니다. 이러한 값들은 $1, $2, $3 등과 같은 위치 매개변수로 전달됩니다.
까다로운 부분은 $1을 읽는 것이 아닙니다. 까다로운 부분은 누락된 인자, 공백이 있는 인자, 선택적 플래그를 처리하고 스크립트가 "나만을 위한" 것에서 다른 사람이 새벽 2시에 실행할 것으로 성장하는 순간을 다루는 것입니다.
위치 매개변수의 구조
위치 매개변수는 셸에 의해 정의된 특수 변수로, 스크립트 이름 뒤 명령줄에 제공된 단어에 해당합니다. 1부터 순차적으로 번호가 매겨집니다.
| 매개변수 | 설명 | 예시 값 (./script.sh file1 dir/ 실행 시) |
|---|---|---|
$0 |
스크립트 자체(또는 함수)의 이름입니다. | ./script.sh |
$1 |
스크립트에 전달된 첫 번째 인자입니다. | file1 |
$2 |
스크립트에 전달된 두 번째 인자입니다. | dir/ |
$N |
N번째 인자입니다 (N > 0). | |
${10} |
9를 초과하는 인자는 중괄호로 묶어야 합니다. |
$9 이후의 인자에 접근하기
1부터 9까지의 인자는 $1부터 $9로 직접 접근할 수 있지만, 10번째 인자 및 그 이후의 인자에 접근하려면 환경 변수나 문자열 연산과의 모호성을 방지하기 위해 숫자를 중괄호로 묶어야 합니다 (예: $10 대신 ${10}).
스크립팅을 위한 필수 특수 매개변수
숫자 매개변수 외에도 Bash는 인자 집합 전체와 관련된 몇 가지 중요한 특수 변수를 제공합니다. 이는 검증과 반복에 필수적입니다.
$#로 인자 개수 세기
특수 변수 $#는 스크립트에 전달된 명령줄 인자의 총 개수($0 제외)를 저장합니다. 이는 입력 검증을 구현하는 데 가장 중요한 변수일 것입니다.
#!/bin/bash
if [ "$#" -eq 0 ]; then
echo "오류: 인자가 제공되지 않았습니다."
echo "사용법: $0 <입력_파일>"
exit 1
fi
echo "$#개의 인자를 제공했습니다."
모든 인자: $@와 $*
$@와 $* 변수는 모두 전체 인자 목록을 나타내지만, 특히 따옴표로 묶였을 때 다르게 동작합니다.
$* (단일 문자열)
큰따옴표로 묶이면("$*"), 위치 매개변수의 전체 목록이 IFS(내부 필드 구분자) 변수의 첫 번째 문자(보통 공백)로 구분된 단일 인자로 처리됩니다.
- 입력 인자가
arg1arg2arg3인 경우 "$*"는"arg1 arg2 arg3"(하나의 요소)로 확장됩니다.
$@ (개별 문자열 - 권장)
큰따옴표로 묶이면("$@"), 각 위치 매개변수가 개별적이고 따옴표로 묶인 인자로 처리됩니다. 이는 인자를 반복하는 표준적이고 권장되는 방법이며, 공백이 포함된 인자를 올바르게 보존합니다.
- 입력 인자가
arg1"공백이 있는 인자"arg3인 경우 "$@"는"arg1" "공백이 있는 인자" "arg3"(세 개의 개별 요소)로 확장됩니다.
따옴표가 중요한 이유: 데모
다음 인자로 실행된 스크립트를 고려해보세요: ./test.sh 'hello world' file.txt
#!/bin/bash
# 따옴표 없는 $*는 공백으로 분할되며 일반적으로 잘못되었습니다.
echo "-- 따옴표 없는 $*를 사용한 반복 --"
for item in $*; do
echo "항목: $item"
done
# 따옴표로 묶인 "$@"는 각 원래 인자를 보존합니다.
echo "-- 따옴표로 묶인 $@를 사용한 반복 --"
for item in "$@"; do
echo "항목: $item"
done
./test.sh 'hello world' file.txt를 사용하면 따옴표 없는 루프는 hello와 world를 별도의 항목으로 출력합니다. "$@" 루프는 hello world를 하나의 인자로 유지합니다. 이러한 차이점이 숙련된 셸 사용자가 거의 자동으로 "$@"를 사용하는 이유입니다.
인자 처리를 위한 실용적인 기술
1. 기본 인자 검색 스크립트
이 간단한 스크립트는 특정 매개변수에 접근하고 $0을 사용하여 유용한 피드백을 제공하는 방법을 보여줍니다.
deploy_service.sh:
#!/bin/bash
# 사용법: deploy_service.sh <서비스_이름> <환경>
SERVICE_NAME="$1"
ENVIRONMENT="$2"
# 검증 확인 (최소 두 개의 인자)
if [ "$#" -lt 2 ]; then
echo "사용법: $0 <서비스_이름> <환경>"
exit 1
fi
echo "서비스 배포 시작: $SERVICE_NAME"
echo "대상 환경: $ENVIRONMENT"
# 검증된 매개변수를 사용하여 명령 실행
ssh admin@server-"$ENVIRONMENT" "/path/to/start $SERVICE_NAME"
2. 강력한 입력 검증
좋은 스크립트는 진행하기 전에 항상 입력을 검증합니다. 여기에는 개수 확인($#)과 종종 인자 내용 확인(예: 인자가 숫자인지 또는 유효한 파일 경로인지 확인)이 포함됩니다.
#!/bin/bash
# 1. 인자 개수 확인 (정확히 3개여야 함)
if [ "$#" -ne 3 ]; then
echo "오류: 이 스크립트는 세 개의 인자(소스, 대상, 사용자)가 필요합니다."
echo "사용법: $0 <src_path> <dest_path> <user>"
exit 1
fi
SRC_PATH="$1"
DEST_PATH="$2"
USER="$3"
# 2. 내용 확인 (예: 소스 경로 존재 여부 확인)
if [ ! -f "$SRC_PATH" ]; then
echo "오류: 소스 파일 '$SRC_PATH'을(를) 찾을 수 없거나 파일이 아닙니다."
exit 2
fi
# 검증 통과 시 진행
echo "$SRC_PATH를 $DEST_PATH로 $USER 사용자로 복사하는 중..."
모범 사례 팁: 검증에 실패할 경우 항상 명확하고 간결한
사용법:문을 제공하세요. 이는 사용자가 명령 호출을 빠르게 수정하는 데 도움이 됩니다.
3. shift로 인자 반복하기
shift 명령은 인자를 순차적으로 처리하는 훌륭한 도구이며, 간단한 플래그를 처리하거나 while 루프 내에서 인자를 하나씩 처리할 때 자주 사용됩니다.
shift는 현재 $1 인자를 버리고, $2를 $1로, $3을 $2로 이동시키며 $#를 1 감소시킵니다. 이를 통해 첫 번째 인자를 처리하고 인자가 남지 않을 때까지 반복할 수 있습니다.
#!/bin/bash
# 간단한 -v 플래그를 처리한 다음 나머지 파일을 나열합니다.
VERBOSE=false
if [ "$1" = "-v" ]; then
VERBOSE=true
shift # -v 플래그를 버리고 인자를 위로 이동
fi
if $VERBOSE; then
echo "상세 모드가 활성화되었습니다."
fi
if [ "$#" -eq 0 ]; then
echo "지정된 파일이 없습니다."
exit 0
fi
echo "$#개의 남은 파일 처리 중:"
for file in "$@"; do
if $VERBOSE; then
echo "파일 확인 중: $file"
fi
# ... 여기에 처리 로직
done
참고:
shift는 간단한 구문 분석에 유용합니다. 많은 플래그가 있는 복잡한 스크립트의 경우, 짧은 옵션에는 일반적으로getopts가 더 적합합니다. 긴 옵션 처리는 플랫폼에 따라 다르므로, 외부getopt를 사용하는 경우 주의해서 테스트하세요.
더 현실적인 파서
많은 내부 스크립트는 하나의 선택적 플래그와 하나의 필수 값으로 시작합니다. 다음은 가독성을 유지하는 작은 패턴입니다:
#!/usr/bin/env bash
set -u
dry_run=false
environment=""
usage() {
echo "사용법: $0 [--dry-run] --env <dev|staging|prod> <file>..." >&2
}
while [ "$#" -gt 0 ]; do
case "$1" in
--dry-run)
dry_run=true
shift
;;
--env)
if [ "$#" -lt 2 ]; then
echo "오류: --env에는 값이 필요합니다." >&2
usage
exit 2
fi
environment="$2"
shift 2
;;
--help|-h)
usage
exit 0
;;
--)
shift
break
;;
-*)
echo "오류: 알 수 없는 옵션: $1" >&2
usage
exit 2
;;
*)
break
;;
esac
done
if [ -z "$environment" ]; then
echo "오류: --env는 필수입니다." >&2
usage
exit 2
fi
if [ "$#" -eq 0 ]; then
echo "오류: 하나 이상의 파일을 제공하세요." >&2
usage
exit 2
fi
for file in "$@"; do
if [ ! -f "$file" ]; then
echo "오류: 파일을 찾을 수 없음: $file" >&2
exit 3
fi
if $dry_run; then
echo "$file을(를) $environment에 업로드할 것입니다."
else
echo "$file을(를) $environment에 업로드 중"
# 업로드 명령이 여기에 위치합니다.
fi
done
지루한 세부 사항에 주목하세요. 오류 메시지는 stderr로 전송됩니다. --는 "옵션 구문 분석 중지"를 의미하므로, 대시로 시작하는 파일 이름을 전달할 수 있습니다. 최종 파일 루프는 "$@"를 사용하므로 release notes.txt는 하나의 파일 이름으로 유지됩니다.
일반적인 실수
가장 흔한 실수는 따옴표를 잊는 것입니다:
cp $1 $2
이는 경로에 공백이나 셸 글로브 문자가 포함된 경우 문제가 발생합니다. 다음을 사용하세요:
cp -- "$1" "$2"
--는 많은 명령에 옵션 구문 분석이 완료되었음을 알리므로, 경로가 -로 시작하는 경우 도움이 됩니다.
또 다른 일반적인 실수는 너무 늦게 검증하는 것입니다. 스크립트가 두 개의 인자를 예상하는 경우, 파괴적인 작업을 수행하기 전에 이를 확인하세요:
if [ "$#" -ne 2 ]; then
echo "사용법: $0 <source> <destination>" >&2
exit 2
fi
호출자에게 도움이 될 때 고유한 종료 코드를 사용하세요. 사용법 오류는 2일 수 있습니다. 누락된 파일은 3일 수 있습니다. 실패한 외부 명령은 자체 상태를 유지할 수 있습니다. 거대한 종료 코드 분류가 필요하지는 않지만, 잘못된 호출 후 0을 반환하면 자동화를 신뢰하기 어렵게 만듭니다.
함수에도 위치 매개변수가 있습니다
Bash 함수 내에서 $1과 $2는 스크립트의 원래 인자가 아닌 함수의 인자를 참조합니다.
log_copy() {
local src="$1"
local dest="$2"
echo "$src를 $dest로 복사 중"
cp -- "$src" "$dest"
}
log_copy "$1" "$2"
이는 유용하지만, 함수 내부의 $1이 스크립트 수준의 첫 번째 인자를 의미할 것이라고 예상했다면 놀랄 수 있습니다. 값을 명시적으로 전달하세요. 이렇게 하면 함수를 테스트하고 재사용하기가 더 쉬워집니다.
다른 명령으로 인자 전달하기
많은 래퍼 스크립트는 다른 명령을 호출하기 전에 약간의 설정을 추가하기 위해 존재합니다. 이 경우 "$@"는 래퍼를 정직하게 유지합니다.
#!/usr/bin/env bash
set -e
export APP_ENV=staging
exec /usr/local/bin/myapp "$@"
누군가 다음을 실행하면:
./run-staging.sh --config "config with spaces.yml" --verbose
래핑된 명령은 동일한 세 개의 인자를 받습니다. $* 또는 따옴표 없는 $@를 사용했다면 구성 경로가 여러 단어로 분할될 수 있습니다.
exec는 선택 사항이지만, 셸 프로세스를 대상 프로세스로 대체하기 때문에 래퍼에서 종종 유용합니다. 이렇게 하면 systemd, Docker 또는 프로세스 감독자 아래에서 신호가 더 예측 가능하게 동작합니다.
놀라움 없는 기본값
때로는 인자가 선택 사항이어야 합니다. Bash 매개변수 확장이 도움이 될 수 있습니다:
environment="${1:-dev}"
이는 "$1이 설정되고 비어 있지 않으면 사용하고, 그렇지 않으면 dev를 사용"을 의미합니다. 이는 친근한 로컬 스크립트에는 괜찮지만, 프로덕션 스크립트에서는 주의하세요. 누군가 인자를 잊어버리면 자동 기본값이 잘못된 환경에 배포될 수 있습니다.
위험한 명령의 경우 명시적 입력을 선호하세요:
if [ "$#" -lt 1 ]; then
echo "사용법: $0 <environment>" >&2
exit 2
fi
기본값은 결과가 작을 때 가장 좋습니다. 예를 들어 로그 수준이나 출력 디렉터리를 기본값으로 설정하는 경우입니다. 인자가 서버를 선택하거나, 데이터를 삭제하거나, 배포 대상을 변경하는 경우에는 위험합니다.
위치 매개변수와 set -u
많은 Bash 스크립트는 설정되지 않은 변수가 오류를 발생시키도록 set -u를 사용합니다. 이는 오타를 잡을 수 있지만, 누락된 위치 매개변수의 동작도 변경합니다.
#!/usr/bin/env bash
set -u
echo "첫 번째 인자: $1"
인자 없이 해당 스크립트를 실행하면 Bash가 "unbound variable" 오류와 함께 종료됩니다. 이 오류는 기술적으로는 맞지만 친근하지 않습니다. 필수 매개변수를 읽기 전에 $#를 검증하세요:
if [ "$#" -lt 1 ]; then
echo "사용법: $0 <input-file>" >&2
exit 2
fi
input_file="$1"
set -u 아래의 선택적 매개변수의 경우 보호된 확장을 사용하세요:
mode="${2:-default}"
이렇게 하면 누락된 선택적 값으로 인해 스크립트가 중단되지 않으면서 엄격 모드를 유용하게 유지할 수 있습니다.
위치 매개변수가 잘못된 인터페이스인 경우
위치 매개변수는 작은 명령에 적합합니다:
backup.sh /var/www /backup/www.tar.gz
스크립트가 많은 값을 받을 때는 읽기 어려워집니다:
deploy.sh prod us-east-1 api v2.4.1 true false 30
아무도 다섯 번째 인자가 무엇을 의미하는지 기억하고 싶어하지 않습니다. 스크립트가 그 지점에 도달하면 명명된 플래그나 구성 파일을 사용하세요:
deploy.sh --env prod --region us-east-1 --service api --version v2.4.1 --timeout 30
코드는 약간 더 길어지지만 명령줄이 자체 문서화됩니다. 이는 팀에서 사용하는 스크립트에 좋은 절충안입니다.
좋은 위치 매개변수 처리는 대부분 규율에 관한 것입니다: 일찍 검증하고, 의도적으로 분할하려는 경우가 아니라면 모든 확장을 따옴표로 묶고, 인자 전달에 "$@"를 사용하고, 사용법 메시지를 트리거하는 검사 가까이에 유지하십시오. 이러한 습관은 작은 스크립트가 실제 파일 이름, 실제 사용자 및 실제 자동화에서 살아남도록 만듭니다.