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

オプションを解析するときに必要な作業の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フィールド分割とワイルドカードを介してのみこれを行うことができ、シェルの完全な解析機能は使用できません。

2つの主張があるとしましょう。フィールド分割のみを使用すると、パラメータで使用可能な文字をさらに制限することなく、これら2つの単語を別々の単語として扱うことはできません(たとえば、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。これは望むものではありません。 (引数の1つがたとえばの場合、上部にワイルドカードが表示されます。)'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を削除し、残りの引数を移動して、次のサイクルで2番目の引数が$ 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 --.

関連情報