Profile picture

[Shell Script] 스크립트 도움말(-h, --help) 기능 만들기

JaehyoJJAng2023년 05월 01일

Bash Script Tempate

#!/usr/bin/bash

set -Eeuo pipefail

# 스크립트 위치 지정
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)

# 도움말 표시
function usage() {
  cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h]

Script description here.

Available options:

-h, --help      Print this help and exit
EOF
  exit
}

# Message
function msg() {
  echo 1>&2 -e "${1-}"
}

function die() {
  local msg="${1}"
  local code="${2-1}"
  msg "${msg}"
  exit "${code}"
}

function parse_param() {
  # default values of variables set from params
  param=''

  while :; do
    case "${1-}" in
      -h | --help) usage ;;
      -?*) die "Unknown option: ${1}" ;;
      *) break ;;
    esac
    shift
  done

  # Get Arguments
  args=("${@}")

  # Check required arguments
  [[ "${#args[@]}" -eq 0 ]] && die "Missing script arguments"
  return 0
}

parse_params "${@}"

1. Bash 선택하기

#!/usr/bin/bash

스크립트는 기본적으로 shebang으로 시작한다. 최적의 호환성을 위하여 /bin/bash가 아닌 /usr/bin/env를 참조하도록 하였다.


2. 스크립트 오류검증

$ set -Eeuo pipefail

set 명령어는 스크립트 실행 옵션을 변경한다. 예를 들어 기본적으로 Bash는 일부 명령이 실패하는 것과 상관없이 0 외의 종료 상태 코드를 반환한다.

  • set -E : 에러가 발생하면 즉시 스크립트를 종료합니다. 에러가 발생하면 명령어의 결과가 0이 아니더라도 스크립트가 계속 진행되지 않습니다.
  • set -u : 선언되지 않은 변수를 사용하면 에러를 발생시킵니다. 이것은 스크립트에서 존재하지 않는 변수를 사용할 때 에러를 방지합니다.
  • set -o pipefail : 파이프(|)로 연결된 명령어들이 모두 성공해야 하며, 그렇지 않은 경우 파이프라인 전체가 실패하도록 합니다.
    • 이 옵션을 사용하게 될 시 코드 내에 입력된 파이프 중 하나라도 실패하면 전체 명령어가 실패합니다.

3. 스크립트 위치 지정

script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd -P)

해당 줄은 스크립트가 위치한 디렉토리를 지정하는데 가장 효과적이다.

스크립트가 작업 디렉토리(Working directory)에 있다면 스크립트가 상대 경로로 동작하며 파일을 복사하고 명령어를 실행한다.
(동일한 디렉토리에서 스크립트 실행 시)


하지만 동일 디렉토리 구조가 아닌 CI 구성(Github Actions 등 ..)에서 다음과 같은 스크립트를 실행한다면 아래처럼 절대경로로 스크립트 파일을 지정해줘야 한다.

bash /tmp/ci/project/script.sh

이 스크립트는 프로젝트 디렉토리가 아닌 CI 도구의 다른 작업 디렉토리에서 동작한다. 스크립트를 실행하기 전 해당 디렉토리로 이동함으로써 해당 문제를 해결할 수 있다.

cd /tmp/ci/project && bash script.sh

그러나 위 방법말고 스크립트 쪽에서 해결하는게 훨씬 좋다. 스크립트가 일부 파일을 읽거나 동일한 디렉토리에서 다른 프로그램을 실행하려는 경우 아래처럼 호출할 수 있다.

cat "${script_dir}/script.sh"

동시에 스크립트는 작업 디렉토리 위치를 변경하지 않는다. 스크립트가 다른 디렉토리에서 실행되어 사용자가 일부 파일에 대한 상대 경로를 제공하더라도 해당 스크립트를 읽을 수 있다.


4. 도움말 표시 함수

function usage() {
  cat <<EOF
Usage: $(basename "${BASH_SOURCE[0]}") [-h]

Script description here.

Available options:

-h, --help      Print this help and exit
EOF
  exit
}

usage() 함수가 상대적으로 스크립트 상단에 있을 경우 다음의 목적으로 동작함.

  • 해당 스크립트에 대한 옵션을 보고싶은 사용자를 위해.

여기에 모든 함수를 기록할 필요는 없다. 그러나 짧고 적절한 스크립트 사용법(usage())는 사용자를 위해서라도 필수적인 항목이다.


5. 파라미터 파싱

function parse_param() {
  while :; do
    case "${1-}" in
      -h | --help) usage ;;
      -?*) die "Unknown option: ${1}" ;;
      *) break ;;
    esac
    shift
  done

  # Get Arguments
  args=("${@}")

  # Check required arguments
  [[ "${#args[@]}" -eq 0 ]] && die "Missing script arguments"
  return 0
}
  • param='' : param 이라는 변수를 빈 문자열로 초기화합니다. 이 변수는 함수 내부에서 파라미터 값을 저장하는 데 사용됩니다.
  • while :; do ... done : 무한 루프를 시작합니다.
  • case "${1-}" in ... esac : $1에 대한 case 문으로, 함수에 전달된 첫 번째 파라미터를 확인합니다.
  • -h | --help) usage ;; : -h 또는 --help 옵션이 전달된 경우 usage 함수를 호출합니다. 이 함수는 도움말 또는 사용법을 표시하는 것으로 추정됩니다.
  • -* ) die "Unknown option: ${1}" ;; : 전달된 옵션이 -로 시작하는데 위에서 정의한 옵션들과 일치하지 않는 경우, 알 수 없는 옵션이라는 메시지와 함께 에러를 출력하고 스크립트를 종료합니다.
  • *) break ;; : 옵션 처리가 끝나면 루프를 종료합니다.
  • shift: 처리한 옵션을 제거하고 다음 파라미터로 이동합니다.
  • args=("${@}"): 파라미터를 배열 args에 저장합니다.
  • [[ "${#args[@]}" -eq 0 ]] && die "Missing script arguments": args 배열의 길이가 0인 경우, 즉 전달된 파라미터가 없는 경우 에러를 출력하고 스크립트를 종료합니다.
  • return 0: 함수를 종료하고 0을 반환합니다. 여기서 0은 성공적으로 함수가 실행되었음을 의미합니다.

Loading script...