getopts 매개변수를 다른 입력과 함께 사용할 수 있습니까?

getopts 매개변수를 다른 입력과 함께 사용할 수 있습니까?

문자열을 입력으로 사용하고 사용자가 매개 변수를 표시기로 사용하여 선택할 수 있는 기타 옵션을 사용하는 스크립트를 작성 중입니다. 즉, 이는 다음과 같습니다.

./script "My input string" -pxz

또는

./script -pxz "My input string"

그러나 다음과 같은 문제에 직면했습니다. getopts 명령이 getopts가 아닌 스타일 인수를 입력한 후 작동이 중지된 것처럼 보입니다. 예를 들면 다음과 같습니다.

#!/bin/bash

while getopts "ab" arg; do
    echo $arg received
done

실행하면 다음을 얻습니다.

$ ./example.sh -a -b -a string -b
a received
b received
a received
$

"문자열"에서 멈추고 계속되지 않습니다. getopts 명령은 "string"이 읽을 것으로 예상되는 인수 유형이 아니기 때문에 0이 아닌 종료 상태를 반환하고 while 루프가 종료됩니다. 두 번째 것을 추가하려고 시도했지만 while getopts아무 효과가 없었습니다. getopts의 "읽기 헤드"는 여전히 해당 "문자열" 매개변수에 고정되어 있으며 0이 아닌 종료 상태로 다시 종료됩니다.

이는 getopts가 문자열을 읽고 거기에 도달할 수 없기 때문에 사용자가 먼저 문자열을 입력한 다음 옵션을 입력하도록 할 수 없다는 것을 의미합니다. 그래서 나는 이것을 할 수 없습니다:

./script "My input string" -pxz

반면에 사용자에게 먼저 옵션을 입력한 다음 문자열을 입력하라고 하면 문자열을 검색하는 방법에 문제가 발생합니다. 일반적으로 선택의 여지가 없으면 다음과 같이 합니다.

string="$1"

하지만 이제 옵션이 있고 얼마나 많은지 모르기 때문에 문자열이 어떤 위치를 차지하고 있는지 더 이상 알 수 없습니다. 그럼 어떻게 다시 돌려받을 수 있나요?

이제 이상적으로는 내 스크립트가 실제로 양방향으로 작동할 수 있거나 두 가지를 조합하여 작동할 수 있어야 합니다. 다음과 같은 입력을 처리할 수 있어야 합니다.

./script -p "My input string" -xz

그렇다면 이 문제를 어떻게 해결해야 할까요?

답변1

getoptLinux를 사용하고 있고 util-linux(또는 Busybox)의 명령이 있는 경우 GNU 도구처럼 인수 순서를 변경할 수도 있습니다. 또한 인수, 옵션 인수, 선택적 옵션 인수 및 --종료 옵션 처리에서 공백을 지원합니다.

표준 동작은 첫 번째 비옵션이 발견되면 옵션 처리를 중지하는 것이므로 다른 구현에서는 혼합 옵션과 비옵션을 지원하지 않을 수 있습니다. 설정 하면 POSIXLY_CORRECTGNU에서도 지원하지 않습니다.

어쨌든 다음 스크립트를 사용하십시오.

$ ./opts.sh -a 123 "arg with space" more -bc args -- -also -args
option -a with arg '123'
option -b
option -c
remaining arguments (5):
<arg with space> <more> <args> <-also> <-args> 

opts.sh:

#!/bin/bash

getopt -T
if [ "$?" -ne 4 ]; then
    echo "wrong version of 'getopt' installed, exiting..." >&2
    exit 1
fi 
params="$(getopt -o a:bc -- "$@")"
eval set -- "$params"
while [ "$#" -gt 0 ]; do
    case "$1" in
    -a)
        echo "option -a with arg '$2'"
        shift 2;;
    -b)
        echo "option -b"
        shift;;
    -c)
        echo "option -c"
        shift;;
    --) 
        shift
        break;;
     *) 
        echo "something else: '$1'"
        shift;;
    esac
done
echo "remaining arguments ($#):"
printf "<%s> " "$@"
echo

(위 스크립트는 getoptutil-linux 개선 사항과 호환되는 구현에서만 작동한다는 점에 유의하십시오. 다른 "레거시" 구현에는 매개변수 등의 공백 문제가 있습니다. 이 getopt -T테스트는 구현이 호환되는지 확인하는 데 사용됩니다.)

답변2

이 페이지 (getopts 이후 스크립트 매개변수 구문 분석) 이것을 이해하는 과정에서 많은 도움이 되었습니다. 하지만 처음부터 모든 것을 설명하려고 노력할 것입니다.

터미널에서 무엇이든 실행되면 이를 호출하기 위한 모든 인수가 포함된 배열을 받습니다. 첫 번째 매개변수는 이 배열에서 위치 0을 차지하며 항상 프로그램 또는 스크립트 자체의 이름입니다(때때로 해당 경로와 함께 사용되지만 터미널에 입력됨). Bash 스크립트에서는 번호가 매겨진 변수를 통해 이 배열에 액세스할 수 있습니다 $0. $n여기서 n은 우리가 받은 인수의 수입니다. 예를 들어 스크립트가 다음과 같이 호출된 경우:

./script --the -great "brown fox" --------jumped

그런 다음 스크립트에서 다음을 수행합니다.

$0 = ./스크립트

$1 = --the

2달러 = - 대단해

$3 = 갈색여우

$4 = --------점프

그렇다면 다음과 같이 하세요.

echo $3

우리는 다음을 얻었습니다:

brown fox

이제 쉘은 내부 변수를 사용하여 배열이 시작되는 위치를 추적합니다. 모든 숫자 변수( 제외 $0)는 이 배열의 시작 부분에서 오프셋됩니다. 이 shift명령을 사용하면 배열의 시작 부분을 이동하여 이전 위치에서 하나 이상의 위치 아래로 시작할 수 있습니다. 이는 $1이전 상황을 참조 할 수 있습니다 $2. 따라서 이 예에서는 다음과 같이 합니다.

shift
echo $1
echo $2

우리는 얻을 것이다

-great
brown fox

이 전체 변환에 대한 유일한 예외는 입니다 $0. $0절대 움직이지 마세요. 여기에는 항상 스크립트를 호출하는 데 사용한 경로가 포함됩니다.

shift명령은 숫자를 지정할 수도 있습니다. 이는 배열을 이동하는 위치의 수입니다. 따라서 두 번 shift 2사용하는 것과 같습니다 shift(즉, 기본 숫자는 1입니다).

명령 에는 진행 상황을 추적하는 데 사용하는 getopts자체 변수가 있습니다 . 인수를 성공적으로 가져오면(또는 인수의 마지막 문자 옵션(예: -abc) 가져오기를 완료하면) 명령줄에서 다음 인수를 가리키도록 하나를 추가 OPTIND합니다 . 다음 에 호출되면 다음 인수부터 구문 분석이 시작됩니다.getoptsOPTINDgetopts

이제 재미있는 부분이 있습니다. in 의 숫자는 OPTIND배열 시작 부분의 오프셋도 나타냅니다. 따라서 이 shift명령을 사용하여 다음에 영향을 줄 수 있습니다 getopts.

#!/bin/bash

getopts "abc" arg
echo received $arg
shift
getopts "abc" arg
echo received $arg

실행하면:

./script -a -b -c

우리는 다음을 얻었습니다:

received a
received c

무슨 일이 일어나는가: OPTIND각 스크립트의 시작 부분에 값 1이 저장됩니다. 즉 getopts, 처음 호출하면 배열 시작 부분에서 1번째 위치에 있는 매개변수인 매개변수 1부터 읽기 시작합니다(기본적으로 로 읽습니다 $1). 변수가 처음 호출된 후에는 매개변수를 가리키는 값 2를 getopts보유합니다 . 하지만 그런 다음 교대를 수행하면 이제 이전 교대를 가리키게 됩니다 . 이제 값을 변경하지 않고 를 가리킵니다 .OPTIND-b$2$3OPTIND-c

이것이 우리에게 어떻게 도움이 됩니까? 글쎄, 우리는 in OPTIND(우리가 액세스할 수 있는)의 값을 사용하여 배열을 이동할 수 있으며, 구문 분석된 모든 매개변수를 건너뛰고 $1아직 구문 분석되지 않은 첫 번째 매개변수를 생성할 수 있습니다. 생각해 보세요:

#!/bin/bash

while getopts "abc" arg; do
    echo recieved $arg
done

shift $((OPTIND-1))

echo $1

실행하면:

./script -a -b -a -b string -a

앞에 옵션을 아무리 많이 놓아도 echo항상 맨 아래 옵션이 출력됩니다. string이는 while 루프가 OPTIND완료된 후 항상 문자열을 가리키기 때문입니다. 그런 다음 산술 확장을 사용하여 shift문자열 위치보다 하나 작은 숫자를 전달합니다 . 이는 $1해당 위치로 이동한다는 의미입니다. 따라서 echo는 $1항상 문자열을 에코합니다.

문자열을 만난 후에도 계속해서 옵션을 읽을 수도 있습니다. 주목할 점은 OPTIND변환 후에도 여전히 그 가치를 유지한다는 것입니다. 따라서 OPTIND5에 도달 하면 $1문자열인 시프트를 한 다음 getopts우리가 원했던 것처럼 문자열 뒤의 5번째 인수부터 시작하는 대신 문자열 뒤의 5번째 인수에서 읽기를 시작할 위치로 즉시 돌아갑니다. . 이 문제를 해결하려면 간단히 2로 설정하면 됩니다 OPTIND.

OPTIND=2

또는 Shift하고 1로 설정합니다(따라서 현재 $1문자열 뒤의 인수를 읽습니다).

shift
OPTIND=1

모든 것을 종합하면 모든 옵션을 읽고 다른 모든 입력의 배열을 생성하는 코드는 다음과 같습니다.

#!/bin/bash

while [[ $# -gt 0 ]]; do
    
    while getopts "abc" arg; do
        echo recieved $arg
    done
    
    shift $((OPTIND-1))
    

    # when we get here we know we have either hit the end of all
    # arguments, or we have come to one that is an
    # input string (not an option)
    # so see which it is we test if $1 is set
    if [[ ${1+set} = set ]]; then
        INPUTS+=("$1")
        shift
    fi
    
    OPTIND=1
done


echo "Here is the array:"
echo ${INPUTS[@]}

관련 정보