| 변수 | 뜻 |
|---|---|
$0 |
스크립트 이름 |
$1 ~ $9 |
첫 번째부터 아홉 번째 인자. 10번째부터는 ${10} |
$# |
인자 개수 |
"$@" |
인자 전체. 각각을 따로 따옴표로 감싼 것과 같다 |
$* |
인자 전체를 하나의 문자열로 이어 붙인 것 |
$? |
직전 명령의 종료 코드 |
"$@" 와 $* 를 혼동하지 않는다. 공백이 든 인자를 그대로 넘기려면 반드시 따옴표를 붙인 "$@" 를 쓴다.
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -lt 2 ]; then
echo "사용법: $0 <환경> <대상> [옵션]" >&2
exit 1
fi
env_name=$1
target=$2
shift 2
extra=("$@")
echo "환경=${env_name} 대상=${target} 나머지=${extra[*]:-없음}"
set -u 를 쓰면 없는 변수를 참조할 때 멈춘다. 선택 인자는 기본값을 붙인다.
log_level=${3:-INFO}
짧은 옵션만 쓰면 getopts 가 표준이고 간결하다.
usage() { echo "사용법: $0 -e <환경> -t <대상> [-v]" >&2; exit 1; }
verbose=0
while getopts ":e:t:v" opt; do
case "$opt" in
e) env_name=$OPTARG ;;
t) target=$OPTARG ;;
v) verbose=1 ;;
\?) echo "알 수 없는 옵션: -$OPTARG" >&2; usage ;;
:) echo "옵션 -$OPTARG 에 값이 필요하다" >&2; usage ;;
esac
done
shift $((OPTIND - 1))
: "${env_name:?-e 가 필요하다}"
: "${target:?-t 가 필요하다}"
맨 앞의 : 는 오류를 직접 처리하겠다는 표시다. 이것이 없으면 getopts 가 자기 메시지를 출력한다.
getopts 는 긴 옵션(--env)을 지원하지 않는다. 필요하면 직접 while 로 파싱하거나 GNU getopt 를 쓴다. 이식성을 고려하면 짧은 옵션으로 맞추는 편이 간단하다.
export APP_NAME=appname
export $APP_NAME_HOME=$APP_NAME/home # 동작하지 않는다
셸은 $APP_NAME_HOME 을 APP_NAME_HOME 이라는 이름 하나로 읽는다. 변수 이름 자리에 치환을 넣는 문법은 없다.
APP_NAME=appname
appname_HOME=/opt/appname
var="${APP_NAME}_HOME"
echo "${!var}" # /opt/appname
${!var} 는 bash 확장이다. POSIX sh 에는 없다.
declare -x "${APP_NAME}_HOME=/opt/${APP_NAME}"
printf -v "${APP_NAME}_HOME" '%s' "/opt/${APP_NAME}"
eval "export ${APP_NAME}_HOME=..." 도 되지만, 값에 외부 입력이 섞이면 임의 명령 실행으로 이어진다. declare 나 printf -v 를 쓴다.
동적 변수 이름은 읽기 어렵고 오타를 잡아 주지 못한다. bash 4 이상이면 연관 배열로 표현한다.
declare -A APP_HOME
APP_HOME[appname]=/opt/appname
APP_HOME[otherapp]=/opt/otherapp
echo "${APP_HOME[appname]}"
for name in "${!APP_HOME[@]}"; do
echo "$name -> ${APP_HOME[$name]}"
done
macOS 기본 bash 는 3.2 라 연관 배열이 없다. 대상 환경을 확인한다.
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
-e 는 실패 시 중단, -u 는 없는 변수 참조 시 중단, -o pipefail 은 파이프 중간 실패를 종료 코드에 반영한다. set -e 에는 예외가 많으므로(조건문 안, && 뒤 등) 중요한 지점은 명시적으로 확인한다.
if ! command -v jq >/dev/null 2>&1; then
echo "jq 가 필요하다" >&2
exit 1
fi