Bash: 명령줄 인수를 구문 분석하는 스크립트에서 eval 및 Shift를 사용하는 이유는 무엇입니까?

Bash: 명령줄 인수를 구문 분석하는 스크립트에서 eval 및 Shift를 사용하는 이유는 무엇입니까?

내가 이 답을 찾고 있었을 때https://stackoverflow.com/a/11065196/4706711--something예를 들어 매개변수를 사용하는 방법 이나 -s응답 스크립트에 대해 묻는 일부 질문을 알아내려면 다음을 수행하세요 .

#!/bin/bash
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
     -n 'example.bash' -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

while true ; do
    case "$1" in
        -a|--a-long) echo "Option a" ; shift ;;
        -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
        -c|--c-long) 
            # c has an optional argument. As we are in quoted mode,
            # an empty parameter will be generated if its optional
            # argument is not found.
            case "$2" in
                "") echo "Option c, no argument"; shift 2 ;;
                *)  echo "Option c, argument \`$2'" ; shift 2 ;;
            esac ;;
        --) shift ; break ;;
        *) echo "Internal error!" ; exit 1 ;;
    esac
done
echo "Remaining arguments:"
for arg do echo '--> '"\`$arg'" ; done

먼저, shift다음 줄의 프로그램은 무엇을 수행합니까?

        -a|--a-long) echo "Option a" ; shift ;;

eval그렇다면 다음 명령줄을 사용하는 목적은 무엇입니까?

eval set -- "$TEMP"

위에서 언급한 스크립트의 해당 줄에 주석을 달았고 다음과 같은 응답을 받았습니다.

$ ./getOptExample2.sh  -a 10 -b 20 --a-long 40 -charem --c-long=echi
Param: -a
Option a
Param: 10
Internal error!

하지만 주석을 제거하면 매력처럼 작동합니다.

Option a
Option b, argument `20'
Option a
Option c, argument `harem'
Option c, argument `echi'
Remaining arguments:
--> `10'
--> `40'

답변1

옵션을 구문 분석할 때 해야 할 많은 작업 중 하나 getopt는 옵션이 아닌 인수가 마지막에 있고 결합된 짧은 옵션이 분리되도록 인수를 재배열하는 것입니다. ~에서man getopt:

Output is generated for each element described in the previous section.
Output is done in the same order as the elements are specified  in  the
input,  except  for  non-option  parameters.   Output  can  be  done in
compatible (unquoted) mode, or in such way that  whitespace  and  other
special  characters  within  arguments  and  non-option  parameters are
preserved (see QUOTING).  When the output is  processed  in  the  shell
script,  it  will  seem to be composed of distinct elements that can be
processed one by  one  (by  using  the  shift  command  in  most  shell
languages).

[...]

Normally, no  non-option  parameters  output  is  generated  until  all
options  and  their  arguments  have  been  generated.   Then  '--'  is
generated as a single parameter, and after it the non-option parameters
in  the  order  they were found, each as a separate parameter.

이 효과는 옵션이 루프를 처리하는 코드에 반영됩니다.가설모든 옵션 인수(옵션 인수 포함)는 처음에 단독으로 나타나고 마지막에 옵션이 아닌 인수가 옵니다.

따라서 TEMP재배열, 인용, 분할 및 eval set스크립트 매개변수 만들기 사용 옵션이 포함되어 있습니다.

eval? 의 출력을 안전하게 변환하는 방법이 필요합니다 getopt. 이는 공백, '( "따옴표) *등과 같은 특수 문자를 안전하게 처리한다는 것을 의미합니다 . 이렇게 하려면 getopt 셸에서 해석할 수 있도록 출력에서 ​​해당 항목을 이스케이프 처리하세요. 그렇지 않은 경우 eval유일한 옵션은 이지만 set $TEMP필드 분할 및 와일드카드를 통해서만 이를 수행할 수 있으며 셸의 전체 구문 분석 기능은 사용할 수 없습니다.

두 가지 주장이 있다고 가정해 보겠습니다. 필드 분할만 사용하면 매개변수에서 사용 가능한 문자를 추가로 제한하지 않고 이 두 단어를 별도의 단어로 처리할 수 없습니다(예를 들어 IFS를 로 설정한 경우 매개변수에서 사용할 :수 없습니다 ). :따라서 이러한 문자를 이스케이프 처리하고 쉘이 해당 이스케이프를 해석하도록 할 수 있어야 합니다. 이것이 바로 eval이것이 필요한 이유입니다. 뭔가 크게 잘못되지 않는 한 getopt안전할 것입니다.


의 경우 shift항상 하는 일을 수행합니다. 첫 번째 인수를 제거하고옮기다모든 매개변수( $2현재 내용은 $1)입니다. 이렇게 하면 처리된 인수가 제거되므로 이 루프 후에는 옵션이 아닌 인수만 남게 되며 $@옵션에 대해 걱정할 필요 없이 편리하게 사용할 수 있습니다.

답변2

다음으로, 다음 줄에서 eval 명령을 사용하는 목적은 무엇입니까?

eval set -- "$TEMP"

util-linux 버전 getopt에서 생성된 출력은 셸에 대한 입력으로 사용될 수 있습니다. 공백이 포함된 문자열을 따옴표로 묶고 리터럴 따옴표 및 기타 특수 문자의 이스케이프를 처리합니다.

예를 들어

$ getopt -o a:b -- -a 'foo bar' -b "single'quotes'here"
 -a 'foo bar' -b -- 'single'\''quotes'\''here'

따옴표는 일반 확장의 결과로 처리되지 않지만 전체 구문 분석이 필요합니다. 그게 다야 eval.

출력이 에 할당된 경우 $tmp, 이후의 eval set -- "$tmp"위치 인수 $1, $2, ... 는 -a, foo bar -b -- single'quotes'here및 를 포함하며 루프에서 비교적 쉽게 처리할 수 있습니다.

을 사용하면 set -- $tmp위치 매개변수가 , 등으로 설정됩니다 -a. 이는 원하는 것이 아닙니다. (인수 중 하나가 예를 들어인 경우 맨 위에 와일드카드가 표시됩니다 .)'foobar'*

질문은 다음과 같습니다.변수에 저장된 명령을 어떻게 실행할 수 있나요?, 두 경우 모두 임의의 문자열 목록이 포함됩니다.


이 동작은 getopt쉘 인용 출력을 생성합니다.util-linux 버전에만 해당그것은. 다른 시스템에는 일반적으로 getopt사용되는아니요 eval, 그리고 공백을 포함하거나 와일드카드처럼 보이는 인수를 행복하게 중단합니다. 이와 마찬가지로 출력에서 foo bar​​단일 인수가 무엇인지 알 수 있는 방법이 없습니다 .

$ getopt a:b -a 'foo bar' -b "single'quotes'here"
 -a foo bar -b -- single'quotes'here

를 사용할 때 먼저 / 옵션을 getopt사용하여 임의의 문자열을 처리할 수 있는 안전한 버전이 있는지 확인하세요.-T--test

답변3

오류가 발생하면 스크립트는 제대로 작동합니다 -a 10. 이 -a옵션에는 이 스크립트에 매개변수가 필요하지 않습니다. 만 사용해야 합니다 -a.

매뉴얼 페이지에 설명된 변환은 다음과 같습니다.

shift [n]
              The positional parameters from n+1 ... are renamed to $1 ....  Parameters represented by the numbers $# down to $#-n+1 are unset.  n must be a non-negative number less than or equal to $#.  If  n  is
              0,  no  parameters are changed.  If n is not given, it is assumed to be 1.  If n is greater than $#, the positional parameters are not changed.  The return status is greater than zero if n is greater
              than $# or less than zero; otherwise 0.

따라서 기본적으로 -a를 제거하고 나머지 인수를 이동하여 다음 주기에서 두 번째 인수가 $1이 되도록 합니다.

--매뉴얼 페이지에도 설명되어 있습니다.

 --        A -- signals the end of options and disables further option processing.  Any arguments after the -- are treated as filenames and arguments.  An argument of - is equivalent to --.

관련 정보