배포 스크립트는 인자를 하나도 주지 않아도 기본값으로 돌고, 일부만 줘도 나머지는 기본값을 쓰며, 순서를 바꿔 줘도 동작해야 쓸 만하다. 위치 인자($1, $2)만으로는 이 요구를 채울 수 없다.
인자 하나짜리 간단한 스크립트라면 파라미터 확장으로 충분하다.
#!/usr/bin/env bash
BRANCH="${1:-development}"
echo "branch: ${BRANCH}"
${1:-기본값} 은 $1 이 비었거나 설정되지 않았을 때 기본값을 쓴다. ${1-기본값} 은 설정되지 않았을 때만 쓰고 빈 문자열은 그대로 둔다. 빈 문자열을 의미 있는 값으로 취급할 일이 아니면 :- 를 쓴다.
bash 내장 getopts 는 숏 옵션만 다룬다. 롱 옵션까지 받으려면 while 로 직접 돈다. 표준 도구만 쓰므로 getopt(외부 명령, BSD 계열과 동작이 다름)에 의존하지 않는다는 장점도 있다.
#!/usr/bin/env bash
set -euo pipefail
NAMESPACE="default"
BRANCH="development"
APPLICATION_NAME="myapp"
usage() {
echo "Usage: $0 [-n|--namespace NS] [-b|--branch BR] [-a|--application APP]"
}
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--namespace) NAMESPACE="$2"; shift 2 ;;
-b|--branch) BRANCH="$2"; shift 2 ;;
-a|--application) APPLICATION_NAME="$2"; shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;;
esac
done
echo "NAMESPACE=${NAMESPACE} BRANCH=${BRANCH} APPLICATION_NAME=${APPLICATION_NAME}"
세 가지가 모두 된다.
./deploy.sh
./deploy.sh -n prod
./deploy.sh --namespace prod --branch feature/login --application shop
위 형태는 --namespace 뒤에 값이 없으면 $2 가 비어 있고, set -u 아래에서는 그대로 오류가 난다. 메시지를 제대로 내려면 검사를 넣는다.
-n|--namespace)
[[ $# -ge 2 && "$2" != -* ]] || { echo "--namespace requires a value" >&2; exit 1; }
NAMESPACE="$2"; shift 2 ;;
docker build --build-arg "APP=${APPLICATION_NAME}" -t "${APPLICATION_NAME}:latest" .
kubectl -n "${NAMESPACE}" rollout status deploy/"${APPLICATION_NAME}"
변수는 항상 큰따옴표로 감싼다. 값에 공백이 들어가면 따옴표 없이는 단어가 쪼개진다. 인자 전체를 다른 명령에 그대로 넘길 때는 "$@" 를 쓴다. $* 는 하나의 문자열로 합쳐지므로 다르다.
#!/usr/bin/env bash
set -euo pipefail
BRANCH="${1:-development}"
TARGET_DIR="${2:-./app}"
REPO="git@git.example.com:team/app.git"
if [[ -d "${TARGET_DIR}/.git" ]]; then
CURRENT_BRANCH="$(git -C "${TARGET_DIR}" rev-parse --abbrev-ref HEAD)"
git -C "${TARGET_DIR}" fetch origin "${BRANCH}"
LOCAL_HASH="$(git -C "${TARGET_DIR}" rev-parse HEAD)"
REMOTE_HASH="$(git -C "${TARGET_DIR}" rev-parse "origin/${BRANCH}")"
if [[ "${CURRENT_BRANCH}" == "${BRANCH}" && "${LOCAL_HASH}" == "${REMOTE_HASH}" ]]; then
echo "already up-to-date on ${BRANCH}"
exit 0
fi
rm -rf "${TARGET_DIR}"
fi
git clone -b "${BRANCH}" --single-branch "${REPO}" "${TARGET_DIR}"
디렉터리를 오가는 대신 git -C <경로> 를 쓴다. cd 로 이동한 뒤 cd .. 로 돌아오는 방식은 중간에 스크립트가 죽으면 현재 위치가 어긋난 채 남는다.
rm -rf "${TARGET_DIR}" 은 변수가 비면 위험한 경로를 지운다. set -u 와 기본값을 함께 쓰는 이유가 여기에 있다.