如何在只有長選項的 bash 命令列中使用 getopt?

如何在只有長選項的 bash 命令列中使用 getopt?

getoptbash命令列中有一個命令。getopt可以與短選項一起使用(例如getopt -o axby "$@"),並且可以與短選項和長選項一起使用(例如getopt -o axby -l long-key -- "$@"),但現在我需要僅有的長選項(即根本不存在短選項),但是該指令getopt -l long-key -- "$@"無法--long-key正確解析選項。那我該如何使用getopt指令僅有的長選項?或者這是不可能的或只是命令的錯誤getopt

答案1

getopt沒有短期選擇就完全沒問題。但你需要告訴它你沒有短期選擇。這是語法上的一個怪癖——來自手冊:

如果在第一部分中沒有找到-o--options選項,則第二部分的第一個參數將用作短選項字串。

這就是您的測試中發生的情況:getopt -l long-key -- --long-key foo將其--long-key視為選項清單-egklnoyfoo唯一的參數。使用

getopt -o '' -l long-key -- "$@"

例如

$ getopt -l long-key -o '' -- --long-key foo
 --long-key -- 'foo'
$ getopt -l long-key -o '' -- --long-key --not-recognized -n foo
getopt: unrecognized option '--not-recognized'
getopt: invalid option -- 'n'
 --long-key -- 'foo'

答案2

不知道,getoptgetopts內建函數只能用於處理長選項,如下所示:

while getopts :-: o
do  case "$o$OPTARG" in
(-longopt1) process ;;
(-longopt2) process ;;
esac; done

當然,如果長選項應該有參數,那麼這不起作用。不過,這是可以做到的,但是,根據我在這方面的工作所了解到的。雖然我最初將它放在這裡,但我意識到對於長期期權來說它沒有太多用處。在這種情況下,它只是將我的case (match)字段縮短了一個可預測的字元。現在,我知道,它對於短選項非常有用 - 當它循環未知長度的字串並根據其選項字串選擇單個位元組時,它最有用。但當選項for var do case $var inarg,你用它可以做的組合做的事情並不多。我認為,最好保持簡單。

我懷疑情況也是如此,getopt但我對此了解不夠,無法肯定地說。給定以下 arg 數組,我將演示我自己的小 arg 解析器 - 這主要取決於我對aliasand的評估/賦值關係$((shell=math))

set -- this is ignored by default --lopt1 -s 'some '\'' 
args' here --ignored   and these are ignored \
--alsoignored andthis --lopt2 'and 

some "`more' --lopt1 and just a few more

這就是我將要使用的 arg 字串。現在:

aopts() { env - sh -s -- "$@"
} <<OPTCASE 3<<\OPTSCRIPT
acase() case "\$a" in $(fmt='
        (%s) f=%s; aset "?$(($f)):";;\n'
        for a do case "$a" in (--) break;;
        (--*[!_[:alnum:]]*) continue;;
        (--*) printf "$fmt" "$a" "${a#--}";;
        esac;done;printf "$fmt" '--*' ignored)
        (*) aset "" "\$a";;esac
shift "$((SHIFT$$))"; f=ignored; exec <&3 
OPTCASE
aset()  {  alias "$f=$(($f${1:-=$(($f))+}1))"
        [ -n "${2+?}" ] && alias "${f}_$(($f))=$2"; }
for a do acase; done; alias
#END
OPTSCRIPT

它以兩種不同方式之一處理 arg 數組,具體取決於您是否向其提供一組或兩組由--分隔符號分隔的參數。在這兩種情況下,它都適用於 arg 數組的處理序列。

如果你這樣稱呼它:

: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"

它的首要任務是將其acase()函數編寫為如下所示:

acase() case "$a" in 
    (--lopt1) f=lopt1; aset "?$(($f)):";;
    (--lopt2) f=lopt2; aset "?$(($f)):";;
    (--*) f=ignored; aset "?$(($f)):";;
    (*) aset "" "$a";;esac

並在旁邊shift 3acase()當呼叫 shell 建構函數的輸入 here-documents 時,會計算函數定義中的命令替換,但acase()絕不會在呼叫 shell 中呼叫或定義。當然,它是在子 shell 中呼叫的,因此您可以在命令列上動態指定感興趣的選項。

如果你給它一個未分隔的數組,它只會填入acase()以 string 開頭的所有參數的匹配項--

函數幾乎在子 shell 中完成所有處理 - 迭代地將每個 arg 的值保存到分配有關聯名稱的別名。當它完成時,它會列印它保存的每個值alias- 這是 POSIX 指定的列印所有已儲存的值,以這樣的方式引用它們的值,以便它們的值可以重新輸入到 shell。所以當我這樣做時...

aopts --lopt1 --lopt2 -- "$@"

它的輸出如下所示:

...ignored...
lopt1='8'
lopt1_1='-s'
lopt1_2='some '\'' args'
lopt1_3='here'
lopt1_4='and'
lopt1_5='just'
lopt1_6='a'
lopt1_7='few'
lopt1_8='more'
lopt2='1'
lopt2_1='and

some "`more'

當它遍歷 arg 列表時,它會檢查 case 區塊是否匹配。如果它在那裡找到匹配項,它會拋出一個標誌 - f=optname。在它再次找到有效選項之前,它將把每個後續參數添加到它基於當前標誌構建的數組中。如果多次指定相同選項,結果會復合且不會覆蓋。任何不以防萬一的東西 - 或忽略選項後面的任何參數 - 都被分配給被忽略大批。

輸出是 shell 安全的,由 shell 自動進行 shell 輸入,因此:

eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"

……應該是絕對安全的。如果因為任何原因不是安全,那麼您應該向 shell 維護人員提交錯誤報告。

它為每個匹配分配兩種別名值。首先,它設定一個標誌 - 無論選項是否位於不匹配的參數之前,都會發生這種情況。因此--flagarg 清單中的任何出現都會觸發flag=1。這並不復合 ---flag --flag --flag只是得到flag=1。這個值但遞增 - 對於可能跟隨它的任何參數。它可以用作索引鍵。完成上述操作後eval我可以這樣做:

printf %s\\n "$lopt1" "$lopt2"

....要得到...

8
1

所以:

for o in lopt1 lopt2
do list= i=0; echo "$o = $(($o))"
        while [ "$((i=$i+1))" -le "$(($o))" ]
        do list="$list $o $i \"\${${o}_$i}\" "
done; eval "printf '%s[%02d] = %s\n' $list";  done

輸出

lopt1 = 8
lopt1[01] = -s
lopt1[02] = some ' args
lopt1[03] = here
lopt1[04] = and
lopt1[05] = just
lopt1[06] = a
lopt1[07] = few
lopt1[08] = more
lopt2 = 1
lopt2[01] = and

some "`more

對於不匹配的參數我會替換被忽略在上面的for ... in字段中得到:

ignored = 10
ignored[01] = this
ignored[02] = is
ignored[03] = ignored
ignored[04] = by
ignored[05] = default
ignored[06] = and
ignored[07] = these
ignored[08] = are
ignored[09] = ignored
ignored[10] = andthis

相關內容