bash getopts, 짧은 옵션만, 모두 값이 필요함, 본인 확인

bash getopts, 짧은 옵션만, 모두 값이 필요함, 본인 확인

다양한 옵션을 허용하는 쉘 스크립트를 작성하려고 하는데 getopts옵션과 인수의 가변 순서를 처리할 수 있기 때문에 좋은 솔루션인 것 같습니다.

나는 다음과 같이 각 옵션에 해당 값이 필요한 짧은 옵션만 사용하겠습니다. ./command.sh -a arga -g argg -b argb그러나 옵션을 비특정 순서로 입력할 수 있도록 허용하고 싶습니다. 이는 대부분의 사람들이 셸 명령을 사용하는 데 익숙합니다.

case또 다른 점은 옵션 매개변수 값을 직접, 가급적이면 명세서 에서 확인하고 싶다는 점이다 . 그 이유는 진술 :)에 대한 나의 테스트가 일관성 없는 결과를 낳았기 때문입니다(아마도 나의 이해 부족 때문일 것입니다). 예를 들어:case

#!/bin/bash
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
        echo "Usage"
        exit 2
fi
while getopts ":h:u:p:d:" opt; do
        case "$opt" in

                h)
                        MYSQL_HOST=$OPTARG
                        ;;
                u)
                        MYSQL_USER=$OPTARG
                        ;;
                p)
                        MYSQL_PASS=$OPTARG
                        ;;
                d)
                        BACKUP_DIR=$OPTARG
                        ;;
                \?)
                        echo "Invalid option: -$OPTARG" >&2
                        exit 2;;
                :)
                       echo "Option -$OPTARG requires an argument" >&2
                       exit 2;;
        esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST'  MYSQL_USER='$MYSQL_USER'  MYSQL_PASS='$MYSQL_PASS'  BACKUP_DIR='$BACKUP_DIR' Additionals: $@"

무슨 일이 일어나는지는 이것이 실패한다는 것입니다... -d에 인수가 필요하지만 내가 얻는 값이 내가 필요한 값이 아니라는 ./command.sh -d -h
플래그를 지정하고 싶을 때입니다 .-d=-h

그래서 각 옵션이 설정되어 있고 한 번만 설정되었는지 확인하기 위해 Case 문에서 자체 유효성 검사를 실행하는 것이 더 쉬울 것이라고 생각했습니다.

다음을 수행하려고 하는데 내 if [ ! "$MYSQL_HOST" ]; then블록이 실행되지 않습니다.

OPTIND=1 # Reset if getopts used previously

if (($# == 0)); then
        echo "Usage"
        exit 2
fi

while getopts ":h:u:p:d:" opt; do
        case "$opt" in

                h)
                        MYSQL_HOST=$OPTARG
                        if [ ! "$MYSQL_HOST" ]; then
                                echo "host not set"
                                exit 2
                        fi
                        ;;
                u)
                        MYSQL_USER=$OPTARG
                        if [ ! "$MYSQL_USER" ]; then
                                echo "username not set"
                                exit 2
                        fi
                        ;;
                p)
                        MYSQL_PASS=$OPTARG
                        if [ ! "$MYSQL_PASS" ]; then
                                echo "password not set"
                                exit 2
                        fi
                        ;;
                d)
                        BACKUP_DIR=$OPTARG
                        if [ ! "$BACKUP_DIR" ]; then
                                echo "backup dir not set"
                                exit 2
                        fi
                        ;;
                \?)
                        echo "Invalid option: -$OPTARG" >&2
                        exit 2;;
                #:)
                #       echo "Option -$opt requires an argument" >&2
                #       exit 2;;
        esac
done
shift $((OPTIND-1))

echo "MYSQL_HOST='$MYSQL_HOST'  MYSQL_USER='$MYSQL_USER'  MYSQL_PASS='$MYSQL_PASS'  BACKUP_DIR='$BACKUP_DIR' Additionals: $@"

OPTARGan 안의 길이가 0인지 확인할 수 있는 방법이 없나요 getopts ... while ... case?

외부에서 매개변수 검증을 수행하는 것에 의존하고 싶지 않습니다. 그러면 etc getopts에 대한 매개변수 값을 얻고 누락된 옵션을 캡처하지 못할 수도 있습니다.:)while ... case ... esac
-d

답변1

getoptit다음을 사용하여 두 번째 스크립트(내가 로 저장한) 를 호출할 때 :

getoptit -d -h

그러면 다음이 인쇄됩니다:

MYSQL_HOST=''  MYSQL_USER=''  MYSQL_PASS=''  BACKUP_DIR='-h' Additionals: 

if [ ! "$BACKUP_DIR" ]; then이렇게 BACKUP_DIR이 설정되어 있지 않은 경우에는 테스트해 보시기 바랍니다. 그러면 내부의 코드가 실행되지 않는 것이 정상입니다.

각 옵션이 한 번 설정되었는지 테스트하려면 $OPTARG 값을 기준으로 할당하기 전에 이 작업을 수행해야 합니다. 또한 할당하기 전에 $OPTARG가 a로 시작하는지 확인해야 합니다 '-'( -d -h오류의 경우).

...
            d)
                    if [ ! -z "$BACKUP_DIR" ]; then
                            echo "backup dir already set"
                            exit 2
                    fi
                    if [ z"${OPTARG:0:1}" == "z-" ]; then
                            echo "backup dir starts with option string"
                            exit 2
                    fi
                    BACKUP_DIR=$OPTARG
                    ;;
...

답변2

다른 답변은 실제로 귀하의 질문에 많은 답변을 제공하지 않기 때문에 이는 예상되는 동작이며 방법에 따라POSIX해석 중:

옵션에 옵션 인수가 필요한 경우 getopts 유틸리티는 이를 쉘 변수 OPTARG에 배치해야 합니다. 옵션이 없거나 옵션 매개변수 없이 옵션이 발견되면 OPTARG를 설정 해제해야 합니다. )

그것이 명시된 전부이며, (너무 길지 않은) 이야기를 간략하게 말하면: getopts보이는 옵션을 마술처럼 알지 못하는 완벽하게 타당한 주장입니다.필요하다실제로 옵션 인수가 아닌 또 다른 옵션인 인수가 있습니다. 옵션에 유효한 인수 -h로 전달하는 것을 막을 수 있는 방법은 없습니다 -d. 이는 실제로 getopts인수가 필요한 옵션이 명령줄 끝에 나타나는 경우에만 오류가 발생한다는 것을 의미합니다. 예를 들면 다음과 같습니다.

test.sh:

#!/bin/sh

while getopts ":xy:" o; do
    case "${o}" in
        :) echo "${OPTARG} requires an argument"; exit 1;
    esac
done

예:

$ ./test.sh -y -x
$

$ ./test.sh -x -y
y requires an argument

두 번째 접근 방식이 작동하지 않는 이유는 구문 분석이 getopts여전히 동일하기 때문에 루프에 들어가면 "손상"이 이미 완료되었기 때문입니다.

이 기능을 절대적으로 비활성화하려면 Benubird가 지적했듯이 옵션 인수를 직접 확인하고 유효한 옵션과 같으면 오류를 발생시켜야 합니다.

답변3

계속 사용할 예정이지만 getopts나중에 몇 가지 추가 확인을 수행합니다. (테스트되지 않음)

errs=0
declare -A option=(
    [MYSQL_HOST]="-h"
    [MYSQL_USER]="-u"
    [MYSQL_PASS]="-p"
    [BACKUP_DIR]="-d" 
)
for var in "${!option[@]}"; do
    if [[ -z "${!var}" ]]; then
        echo "error: specify a value for $var with ${option[var]}"
        ((errs++))
    fi
done
((errs > 0)) && exit 1

Bash 버전 4 필요

답변4

먼저 를 사용해야 합니다 if [ -z "$MYSQL_USER" ].

둘째, 할당이 필요하지 않습니다. if [ -z "$OPTARG" ]잘 작동합니다.

셋째, 나는 그것이 당신이 정말로 원하는 것이라고 생각합니다 if [ ${#OPTARG} = 0 ]. ${#x}는 문자열 $x의 길이를 반환하는 bash입니다(자세히 보기여기).

넷째, 직접 확인하는 경우에는 getopts 대신 getopt를 사용하는 것이 더 유연하므로 사용하는 것이 좋습니다.

마지막으로 첫 번째 질문에 답하기 위해 다음과 같이 플래그 목록을 맨 위에 배치하여 플래그가 인수로 전달되는 시기를 감지할 수 있습니다.

args=( -h -u -p -d )

그런 다음 제공된 인수가 다음과 같은 옵션인지 확인하려는 옵션을 확인하십시오.

d)
    BACKUP_DIR=$OPTARG
    if [ "$(echo ${args[@]/$OPTARG/})" = "$(echo ${args[@]})" ]
    then
        echo "Argument is an option! Error!"
    fi

완벽한 답변은 아니지만 효과가 있습니다! 문제는 "-h"가 완벽하게 유효한 인수이고 유효한 플래그가 아닌 인수를 찾고 있다는 사실을 쉘에 알릴 수 없다는 것입니다.

관련 정보