위치 매개변수로 작성된 스크립트에 명령줄 옵션에 대한 지원을 도입하는 방법은 무엇입니까?

위치 매개변수로 작성된 스크립트에 명령줄 옵션에 대한 지원을 도입하는 방법은 무엇입니까?

현재 다음과 같은 위치 매개변수와 함께 사용하는 스크립트가 있습니다.

./script.sh fname lname address

또한 제공하고 싶지 않은 인수를 건너뛸 수 있도록 이 스크립트를 호출할 때 일반 명령줄 옵션을 지원하고 싶습니다.

./script.sh -f fname -a address

fname유일한 필수 매개변수입니다.

역사적 이유와 자동화로 인해 스크립트는 이전 버전과 호환되어야 합니다.

-f지금까지 내 추측으로는 문자열(공백으로 둘러싸여 있음)을 찾는 것입니다 .

  • 발견되면 플래그를 처리합니다.
  • 그렇지 않은 경우 순차적으로 처리
flags='(\ |^)-f\ '
if [[ $* =~ $flags ]]; then
while [ $# -ne 0 ]
do
    name="$1"
    case "$name" in
        -f)
            shift
            fname=$1
            ;;
        -l)
            shift
            lname=$1
            ;;
        -a)
            shift
            address=$1
            ;;
    esac
    shift
done
else
    fname=${1}
    lname=${2}
    address=${3}
fi

하지만 여기서는 정규식을 사용하여 옵션을 확인해야 하는데 이는 신뢰할 수 없을 수 있습니다.

명령줄 옵션과 위치 매개변수를 결합하는 기본 방법이 있습니까?

답변1

일반적인 명령줄 구문 분석을 구현하는 데 사용됩니다 getopts. 이 구문 분석이 트리거되면 이 사실을 추적하기 위한 플래그를 설정하십시오. 표준 getopts루프 후에 플래그가 설정되지 않은 경우 이전 동작이 반환됩니다.

아래 코드에서 플래그는 변수입니다 new_behavior.

#!/bin/bash

# As written now, this should run without issues with
# /bin/sh too (there are no bash-isms in this code).

unset address
unset fname
unset lname

new_behavior=false

while getopts 'a:f:l:' opt; do
        new_behavior=true

        case $opt in
                a)
                        address=$OPTARG
                        ;;
                f)
                        fname=$OPTARG
                        ;;
                l)
                        lname=$OPTARG
                        ;;
                *)
                        echo 'Error in command line parsing' >&2
                        exit 1
        esac
done

shift "$(( OPTIND - 1 ))"

if ! "$new_behavior"; then
        # Fall back on old behavior.

        fname=$1;       shift
        lname=$1;       shift
        address=$1;     shift
fi

if [ -z "$fname" ]; then
        echo 'Missing mandatory argument "fname"' >&2
        exit 1
fi

# The rest is unimportant to the actual command line parsing code
# and only here for your information and for debugging.

printf 'fname="%s", lname="%s", address="%s"\n' \
        "$fname" "$lname" "$address"

if [ "$#" -gt 0 ]; then
        printf 'Extra argument: "%s"\n' "$@"
fi

사용자가 스크립트의 첫 번째 인수로 옵션을 제공하면 새로운 동작이 트리거됩니다. 옵션이 유효한지 여부는 중요하지 않습니다.

이전 동작 테스트:

$ ./script eff ell addr
fname="eff", lname="ell", address="addr"
$ ./script eff ell addr "hello world"
fname="eff", lname="ell", address="addr"
Extra argument: "hello world"
$ ./script eff
fname="eff", lname="", address=""
$ ./script
Missing mandatory argument "fname"
$ ./script eff -l ell
fname="eff", lname="-l", address="ell"

새로운 동작을 테스트합니다.

$ ./script -a addr -l ell -f eff
fname="eff", lname="ell", address="addr"
$ ./script -a addr -f eff "hello world"
fname="eff", lname="", address="addr"
Extra argument: "hello world"
$ ./script -f eff "hello world"
fname="eff", lname="", address=""
Extra argument: "hello world"
$ ./script -l eff "hello world"
Missing mandatory argument "fname"
$ ./script -f eff -- -l ell -f "eff again"
fname="eff", lname="", address=""
Extra argument: "-l"
Extra argument: "ell"
Extra argument: "-f"
Extra argument: "eff again"

(마지막 예에서 옵션이 아닌 추가 인수는 options 와 분리되어 있으며 ---l번째 인수는 모두 -f옵션이 아닙니다.)

답변2

그럼 왜 사용해야 할까요 if? 매개변수를 확인하면 됩니다.

flaggiven=0
# This is very unsafe - use `getopt` or `getopts`. `shift` can fail - should be checked for errors.
while (($#)); do
    case "$1" in
    -f) fname=$2; flaggiven=1; shift; ;;
    -l) lname=$2; flaggiven=1; shift; ;;
    -a) address=$2; flaggiven=1; shift; ;;
    *) break;
    esac
    shift
done

if (($# > 0)); then
     if ((flaggiven)); then
        echo "ERROR: you have given -f or -l or -a argument and arguments. One or the other, not both!"
        exit 1
     fi
     fname=$1
     lname=$2
     address=$3
else
   if ((!flaggiven)); then
        echo "ERROR: you have to pass -f -l -a flags or pass 3 arguments"
        exit 1
    fi
fi

관련 정보