Bash

Bash:為什麼在解析命令行參數的腳本中使用 eval 和 shift?

  • October 29, 2021

當我在尋找這個答案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'

解析選項時要做的許多事情之一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 它們轉義到輸出中以供 shell 解釋。沒有eval,唯一的選擇是set $TEMP,但是您僅限於通過欄位拆分和通配而不是 shell 的完整解析能力來實現的功能。

假設你有兩個論點。沒有辦法在不額外限制參數中可用的字元的情況下僅使用欄位拆分將這兩個作為單獨的單詞(例如,假設您將 IFS 設置為:,那麼您不能:在參數中使用)。因此,您需要能夠轉義這些字元並讓 shell 解釋轉義,這就是eval需要的原因。除非出現重大錯誤getopt,否則它應該是安全的。


至於shift,它會做它一直做的事情:刪除第一個參數,並移動所有參數(因此$2現在將是$1)。這消除了已處理的參數,因此,在此循環之後,只剩下非選項參數,您可以方便地使用$@而不必擔心選項。

引用自:https://unix.stackexchange.com/questions/383862