강력한 스크립트 작성을 위한 Bash 인자 파싱 마스터하기

Bash 스크립트의 잠재력을 최대한 발휘하려면 인자 파싱을 마스터하세요. 이 포괄적인 가이드는 위치 인자(positional arguments) 사용법, 짧은 옵션(플래그 및 값) 처리를 위한 강력한 내장 유틸리티 `getopts`의 활용법, 그리고 `--verbose`와 같은 최신 긴 옵션(long options)을 위해 `while/case` 루프를 사용하는 효과적인 기법들을 상세히 설명합니다. 오류 처리, 기본값 설정, 명확한 사용자 안내 등 모범 사례를 포함한 전문적이고 유연한 자동화 도구를 구축하는 방법을 배울 수 있습니다.

26 조회수

강력한 스크립트를 위한 Bash 인자 파싱 마스터하기

명령줄 인자는 유연하고 재사용 가능한 Bash 스크립트의 기반입니다. 효과적인 인자 파싱 없이는 스크립트는 미리 정의된 동작만 가능한 경직된 도구로 남게 됩니다. 인자 파싱을 마스터하면 간단한 자동화 작업을 전문적이고 견고하며 사용자 친화적인 명령줄 도구로 변환할 수 있습니다.

이 가이드에서는 Bash에서 위치 인자, 짧은 옵션(플래그) 및 긴 옵션을 처리하는 필수 기법을 탐구하며, 표준 getopts 유틸리티와 사용자 정의 파싱 루프에 초점을 맞춰 스크립트가 복잡한 구성을 우아하게 처리할 수 있도록 보장합니다.


1. 기본: 위치 인자

플래그와 명명된 옵션을 다루기 전에 Bash가 간단한 순차 인자를 어떻게 처리하는지 이해하는 것이 중요합니다.

변수 설명 예시 (스크립트가 ./script.sh foo bar로 호출된 경우)
$0 스크립트 자체의 이름 ./script.sh
$1, $2, ... 첫 번째, 두 번째 등의 위치 인자 foo, bar
$# 위치 인자의 개수 2
$@ 모든 위치 인자, 개별 문자열로 처리됨 foo bar
$* 모든 위치 인자, 단일 문자열로 처리됨 foo bar

예시: 간단한 위치 인자 확인

#!/bin/bash

if [ "$#" -ne 2 ]; then
    echo "사용법: $0 <원본_파일> <대상_디렉토리>"
    exit 1
fi

SOURCE="$1"
DESTINATION="$2"

echo "$SOURCE 를 $DESTINATION 로 복사 중..."
# cp "$SOURCE" "$DESTINATION"

2. getopts를 사용한 표준 파싱 (짧은 옵션)

-v (자세히) 또는 -f <파일>과 같은 옵션이 필요한 전문 스크립트의 경우, 내장 getopts 유틸리티가 순수 Bash에서 가장 정통적이고 신뢰할 수 있는 방법입니다. 짧은 (한 글자) 옵션을 위해 특별히 설계되었습니다.

getopts 작동 방식

getopts는 옵션을 순차적으로 읽어, 지정된 변수(OPT)에 발견된 옵션을 설정하고, 필요한 경우 모든 인자의 값을 OPTARG 변수에 넣습니다.

  • 옵션 문자열 (OPTSTRING): 유효한 옵션을 정의합니다. 옵션이 인자를 필요로 하는 경우 (예: -f filename), 문자 뒤에 콜론(:)을 붙입니다. 예: vho:-v, -h, 그리고 값을 필요로 하는 -o를 허용합니다.
  • OPTIND: 처리할 다음 인자를 추적하는 내부 Bash 인덱스입니다. 1로 초기화되어야 합니다 (기본값이지만 복잡한 스크립트에서는 종종 재설정됨).

실용적인 getopts 템플릿

이 템플릿은 세 가지 옵션: -v (플래그), -h (플래그), 그리고 -f (값 필요 옵션)를 처리합니다.

#!/bin/bash

# --- 기본값 ---
VERBOSE=0
FILENAME="default.txt"

# --- 사용법 함수 ---
usage() {
    echo "사용법: $0 [-v] [-h] [-f <파일>] <입력>"
    exit 1
}

# --- 인자 파싱 루프 ---
while getopts "v:hf:" OPT; do
    case "$OPT" in
        v)
            VERBOSE=1
            echo "자세한 모드가 활성화되었습니다."
            ;;
        h)
            usage
            ;;
        f)
            # OPTARG는 -f에 제공된 인자를 보유합니다.
            FILENAME="$OPTARG"
            echo "출력 파일 이름이 다음과 같이 설정되었습니다: $FILENAME"
            ;;
        \?)
            # 인식되지 않은 옵션 또는 누락된 인자를 처리합니다.
            echo "오류: 잘못된 옵션 -$OPTARG" >&2
            usage
            ;;
        :)
            # 인자가 필요한 옵션에 대해 인자가 누락된 경우를 처리합니다 (예: 파일 이름 없이 -f).
            echo "오류: 옵션 -$OPTARG 에는 인자가 필요합니다." >&2
            usage
            ;;
    esac
done

# --- 위치 인자 시프트 ---
# getopts가 완료된 후, OPTIND는 첫 번째 비옵션 인자의 인덱스를 보유합니다.
# shift를 사용하여 파싱된 모든 옵션을 삭제하고 위치 인자($1, $2 등)만 남깁니다.
shift $((OPTIND - 1))

# 필수 위치 인자 (INPUT)가 있는지 확인합니다.
INPUT_DATA="$1"
if [ -z "$INPUT_DATA" ]; then
    echo "오류: 입력 데이터가 필요합니다."
    usage
fi

echo "---"
echo "입력 처리 중: $INPUT_DATA"
echo "자세한 상태: $VERBOSE"

팁: while getopts 루프 직후에 항상 shift $((OPTIND - 1))를 사용하여 옵션과 나머지 위치 인자를 깔끔하게 분리하십시오.

3. 긴 옵션 (--option) 처리

순수 Bash의 내장 getopts는 짧은 옵션만 지원합니다. 최신 긴 옵션 (예: --verbose, --output-file=data.log)을 처리하려면 whilecaseshift 명령을 사용하여 사용자 정의 파싱 루프를 구현해야 합니다.

이 방법은 더 명시적인 인자 관리가 필요합니다.

사용자 정의 긴 옵션 파서

#!/bin/bash

# --- 기본값 ---
VERBOSE=0
OUTPUT_FILE=""

# 사용법을 표시하는 사용자 정의 함수 (간략하게 하기 위해 생략됨)
# usage() { ... }

while [ "$#" -gt 0 ]; do
    case "$1" in
        --verbose)
            VERBOSE=1
            shift
            ;;
        --output-file)
            # 두 번의 shift 필요: 플래그용 한 번, 값용 한 번
            if [ -z "$2" ]; then
                echo "오류: --output-file 에는 값이 필요합니다."
                exit 1
            fi
            OUTPUT_FILE="$2"
            shift 2
            ;;
        --help)
            usage
            ;;
        -*)
            echo "오류: 알 수 없는 옵션 $1" >&2
            exit 1
            ;;
        *)
            # 첫 번째 위치 인자를 찾았으므로 옵션 파싱 중지
            break
            ;;
    esac
done

# 나머지 인자는 이제 $1, $2 등과 같이 사용할 수 있습니다.
# ... 스크립트 로직 계속 ...

if [ "$OUTPUT_FILE" ]; then
    echo "데이터가 $OUTPUT_FILE 로 리디렉션됩니다."
fi

고급: 키-값 긴 옵션 (--key=value) 처리

인자가 등호로 전달되는 표준 UNIX 스타일을 선호하는 경우, 매개변수 치환을 사용하여 인자를 분할해야 합니다.

while [ "$#" -gt 0 ]; do
    case "$1" in
        --limit=*)
            LIMIT_VAL="${1#*=}" # '=' 까지의 모든 것을 제거합니다.
            echo "Limit 가 $LIMIT_VAL 로 설정되었습니다."
            shift
            ;;
        # ... 다른 옵션 ...
    esac
done

4. 견고한 스크립팅을 위한 모범 사례

A. 짧은 옵션과 긴 옵션 결합

최대 유연성을 위해 숙련된 스크립터는 종종 짧은 옵션에 대한 getopts의 신뢰성과 긴 옵션에 대한 사용자 정의 while/case 루프를 결합합니다. 긴 옵션 루프가 먼저 실행되어 긴 플래그를 소비한 다음, 나머지 인자 (짧은 옵션 포함)가 getopts로 처리됩니다.

그러나 더 깔끔하고 일반적인 패턴은 -o--option 둘 다를 찾고 적절하게 shift하는 견고한 사용자 정의 루프에서 모든 인자를 처리하는 것입니다.

B. 오류 처리 및 사용법

스크립트의 필수 인자와 옵션을 설명하는 명확한 usage 함수를 항상 제공하십시오. 이 함수는 다음 경우에 호출되어야 합니다:

  1. 사용자가 도움을 요청할 때 (-h 또는 --help 등).
  2. 필수 인자가 누락된 경우.
  3. 잘못되거나 알 수 없는 옵션이 제공된 경우.

스크립트가 오류 발생 시 0이 아닌 상태 (exit 1)로 종료되고 오류 메시지를 표준 오류 (>&2)로 출력하는지 확인하십시오.

C. 기본값

스크립트 시작 부분에서 모든 구성 변수를 합리적인 기본값으로 초기화하십시오. 이렇게 하면 선택적 인자가 전달되지 않더라도 스크립트가 예측 가능하게 됩니다.

# 파싱 전에 항상 변수를 초기화하세요
LOG_LEVEL="info"
FORCE=0

결론

인자 파싱을 마스터하면 간단한 Bash 스크립트가 다목적 명령줄 유틸리티로 격상됩니다. 위치 인자 ($1, $2)는 가장 간단한 작업에 충분하지만, 짧은 옵션에 getopts를 사용하면 POSIX 표준 준수와 견고한 파싱이 보장됩니다. 긴 옵션의 경우, shift를 사용한 전용 while 루프는 최신 CLI 도구에 필요한 유연성을 제공합니다. 견고한 파싱, 합리적인 기본값, 명확한 사용법 메시지를 통합함으로써 자동화 스크립트가 훨씬 더 강력하고 관리 가능해집니다.