วิธีการใช้ getopt ในบรรทัดคำสั่ง bash มีตัวเลือกยาว ๆ เท่านั้น?


13

มีgetoptคำสั่งในบรรทัดคำสั่ง bash getoptสามารถใช้กับตัวเลือกสั้น ๆ (เช่นgetopt -o axby "$@") และสามารถใช้กับทั้งตัวเลือกแบบสั้นและแบบยาว (เช่นgetopt -o axby -l long-key -- "$@") แต่ตอนนี้ฉันต้องการตัวเลือกแบบยาวเท่านั้น (เช่นตัวเลือกแบบสั้นไม่มีอยู่จริง) อย่างไรก็ตามคำสั่งgetopt -l long-key -- "$@"ไม่ได้ แยกวิเคราะห์--long-keyตัวเลือกอย่างถูกต้อง ดังนั้นฉันจะใช้getoptคำสั่งด้วยตัวเลือกยาวเท่านั้นได้อย่างไร หรือมันเป็นไปไม่ได้หรือเป็นเพียงข้อผิดพลาดของgetoptคำสั่ง?


คุณแท็กสำหรับภายในgetoptsแต่คุณกำลังใช้/usr/bin/getoptคำสั่ง
Anthon

@Athon ขออภัยฉันใช้แท็กที่ไม่ถูกต้อง แต่ฉันมีชื่อเสียงไม่เพียงพอที่จะเพิ่มแท็กอื่นซึ่งต้องการ 300 ชื่อเสียง อย่างไรก็ตามตอนนี้ฉันได้ลบแท็กที่ไม่ถูกต้องแล้ว
Victor

คำตอบ:


15

getoptดีมากโดยไม่มีตัวเลือกสั้น ๆ แต่คุณต้องบอกว่าคุณไม่มีตัวเลือกสั้น ๆ มันเป็นเรื่องแปลกในไวยากรณ์ - จากคู่มือ:

หากไม่พบ-oหรือไม่มี--optionsตัวเลือกในส่วนแรกพารามิเตอร์แรกของส่วนที่สองจะถูกใช้เป็นสตริงตัวเลือกสั้น ๆ

นั่นคือสิ่งที่เกิดขึ้นในการทดสอบของคุณ: getopt -l long-key -- --long-key fooถือว่า--long-keyเป็นรายการของตัวเลือก-egklnoyและfooเป็นอาร์กิวเมนต์เดียว ใช้

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'

OP กำลังผสมgetoptsและgetoptแพร่เชื้อไปยังคำตอบของคุณหรือไม่? คุณเริ่มต้นด้วยการแสดงความคิดเห็นแล้วกล่าวถึงเท่านั้นgetopts getopt
Anthon

@ อันธพาลคำตอบทั้งหมดของฉันเป็นเรื่องเกี่ยวกับgetoptโปรแกรมจาก GNU coreutils ซึ่งเป็นสิ่งที่เป็นคำถามเกี่ยวกับ ฉันแก้ไขข้อความที่พูดgetoptsแล้ว ขอบคุณ ไม่ได้ทำตัวเลือกนานดังนั้นไม่มีนี้จะนำไปใช้getopts getopts
Gilles 'หยุดชั่วร้าย'

เดิม OP มีgetoptsแท็ก ฉันไม่ต้องการที่จะเปลี่ยนคำตอบของคุณเพราะปกติแล้วคุณจะรู้ดีกว่าสิ่งที่คุณเขียนเกี่ยวกับ :-)
Anthon

ฉันเสียเวลาพยายามคิดเรื่องนี้ไปเกือบชั่วโมง คุณเพิ่งช่วยฉันจิบกาแฟเปล่า ๆ ขอบคุณ ... ให้นำกาแฟนี้ไปใช้ให้ดีขึ้นในตอนนี้ ☕️
dmmd

1

Dunno เกี่ยวกับgetoptแต่getoptsbuiltin สามารถใช้ในการจัดการตัวเลือกยาวเช่นนี้:

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

แน่นอนเช่นเดียวกับที่ใช้ไม่ได้หากตัวเลือกแบบยาวควรมีอาร์กิวเมนต์ มันสามารถทำได้ แต่ แต่อย่างที่ฉันได้เรียนรู้การทำสิ่งนี้ ในขณะที่ฉันรวมมันไว้ที่นี่ในตอนแรกฉันรู้ว่าสำหรับตัวเลือกแบบยาวมันไม่มีประโยชน์มากนัก ในกรณีนี้มันทำให้case (match)ฟิลด์ของฉันสั้นลงด้วยอักขระที่คาดเดาได้เพียงตัวเดียว ตอนนี้สิ่งที่ฉันรู้คือมันยอดเยี่ยมสำหรับตัวเลือกสั้น ๆ - มันมีประโยชน์มากที่สุดเมื่อมันวนลูปมากกว่าสตริงที่มีความยาวไม่ทราบและเลือกไบต์เดียวตามตัวเลือกสตริง แต่เมื่อตัวเลือกคือหาเรื่องมีหลายสิ่งที่คุณทำด้วยfor var do case $var inชุดค่าผสมที่สามารถทำได้ ฉันคิดว่ามันดีกว่าเพื่อให้ง่าย

ฉันสงสัยว่ามันเป็นเรื่องจริงgetoptแต่ฉันก็ไม่รู้พอที่จะพูดด้วยความมั่นใจ ที่กำหนดอาร์เรย์หาเรื่องต่อไปนี้ผมจะแสดงให้เห็นตัวเองเล็ก ๆ น้อย ๆ parser หาเรื่องฉัน - ซึ่งขึ้นอยู่กับความสัมพันธ์ที่ผิวเล็กน้อย / มอบหมายฉันได้มาชื่นชมสำหรับและalias$((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

นั่นคือสตริงหาเรื่องที่ฉันจะทำงานด้วย ขณะนี้:

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 3จาก การทดแทนคำสั่งในacase()นิยามฟังก์ชันจะถูกประเมินเมื่อเชลล์การเรียกใช้สร้างอินพุตของเอกสารที่นี่ แต่acase()จะไม่ถูกเรียกหรือกำหนดในเชลล์การเรียก มันถูกเรียกใน subshell แน่นอนและด้วยวิธีนี้คุณสามารถระบุตัวเลือกที่น่าสนใจในบรรทัดคำสั่งแบบไดนามิก

หากคุณมอบอาร์เรย์ยกเลิกคั่นมันก็ populates ตรงกับข้อโต้แย้งทั้งหมดเริ่มต้นด้วยสตริงacase()--

ฟังก์ชั่นนี้จะทำการประมวลผลทั้งหมดใน subshell - จริง ๆ แล้วจะบันทึกค่าของ arg แต่ละตัวในนามแฝงที่กำหนดด้วยชื่อที่สัมพันธ์กัน เมื่อมันผ่านมันจะพิมพ์ออกมาทุกค่ามันบันทึกด้วยalias- ซึ่งเป็น POSIX- ระบุในการพิมพ์ค่าที่บันทึกไว้ทั้งหมดที่ยกมาในลักษณะที่ค่าของพวกเขาสามารถนำกลับไปเปลือก ดังนั้นเมื่อฉันทำ ...

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'

ในขณะที่มันเดินผ่านรายการหาเรื่องมันจะตรวจสอบกับบล็อกกรณีสำหรับการแข่งขัน หากพบการแข่งขันตรงนั้นจะเป็นการโยนธง - f=optname. จนกว่ามันจะพบตัวเลือกที่ถูกต้องอีกครั้งมันจะเพิ่มแต่ละอาร์กิวเมนต์ที่ตามมาลงในอาร์เรย์ที่สร้างขึ้นโดยอิงตามสถานะปัจจุบัน หากมีการระบุตัวเลือกเดียวกันหลายครั้งสารประกอบผลลัพธ์และจะไม่แทนที่ สิ่งใดที่ไม่ได้อยู่ในกรณี - หรืออาร์กิวเมนต์ใด ๆ ที่ตามตัวเลือกที่ถูกละเว้น - จะถูกกำหนดให้กับอาร์เรย์ที่ถูกละเว้น

เอาต์พุตเป็นแบบเชลล์ที่ปลอดภัยสำหรับเชลล์โดยอัตโนมัติโดยเชลล์ดังนั้น:

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

... ควรจะปลอดภัยอย่างสมบูรณ์แบบ หากไม่ว่าด้วยเหตุผลใด ๆ ก็ตามไม่ปลอดภัยคุณก็ควรยื่นรายงานข้อผิดพลาดกับผู้ดูแลเชลล์ของคุณ

มันกำหนดค่านามแฝงสองชนิดสำหรับแต่ละการแข่งขัน ก่อนอื่นมันจะตั้งค่าสถานะ - สิ่งนี้เกิดขึ้นหรือไม่ตัวเลือกนำหน้าอาร์กิวเมนต์ที่ไม่ตรงกัน ดังนั้นการเกิดขึ้นของใด ๆในรายการหาเรื่องจะเรียก--flag 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

และสำหรับ args ที่ไม่ตรงกับฉันจะแทนที่ละเว้นใน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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.