วิธีใช้ getopts เพื่อแยกวิเคราะห์ตัวเลือก Linux Shell Script
เผยแพร่แล้ว: 2022-06-25
คุณต้องการให้ลินุกซ์เชลล์สคริปต์ของคุณจัดการกับตัวเลือกบรรทัดคำสั่งและอาร์กิวเมนต์ได้อย่างสง่างามมากขึ้นหรือไม่? Bash getopts
ในตัวช่วยให้คุณแยกวิเคราะห์ตัวเลือกบรรทัดคำสั่งได้อย่างมีชั้นเชิง—และก็ง่ายเช่นกัน เราแสดงให้คุณเห็นว่า
แนะนำ getopts ในตัว
การส่ง ค่า ไปยังสคริปต์ Bash นั้นค่อนข้างง่าย คุณเรียกสคริปต์ของคุณจากบรรทัดคำสั่งหรือจากสคริปต์อื่น และระบุรายการค่าของคุณที่อยู่เบื้องหลังชื่อสคริปต์ ค่าเหล่านี้สามารถเข้าถึงได้ภายในสคริปต์ของคุณเป็นตัวแปร โดยเริ่มจาก $1
สำหรับตัวแปรแรก $2
สำหรับตัวแปรที่สอง และอื่นๆ
แต่ถ้าคุณต้องการส่ง ตัวเลือก ไปยังสคริปต์ สถานการณ์จะซับซ้อนขึ้นอย่างรวดเร็ว เมื่อเราพูดถึงตัวเลือก เราหมายถึงตัวเลือก แฟล็ก หรือสวิตช์ที่โปรแกรมอย่าง ls
สามารถจัดการได้ โดยนำหน้าด้วยเครื่องหมาย “ -
” และมักจะทำหน้าที่เป็นตัวบ่งชี้ให้โปรแกรมเปิดหรือปิดฟังก์ชันการทำงานบางอย่าง
คำสั่ง ls
มีตัวเลือกมากกว่า 50 ตัวเลือก ซึ่งส่วนใหญ่เกี่ยวข้องกับการจัดรูปแบบเอาต์พุต ตัวเลือก -X
(จัดเรียงตามนามสกุล) จะจัดเรียงเอาต์พุตตามตัวอักษรตามนามสกุลไฟล์ อ็อพชัน -U
(unsorted) แสดงรายการตามลำดับไดเร็กทอรี
ตัวเลือกเป็นเพียงเท่านั้น—เป็นตัวเลือก คุณไม่ทราบว่าตัวเลือกใด — หากมี— ผู้ใช้จะเลือกใช้ และคุณไม่ทราบในลำดับที่พวกเขาจะแสดงรายการบนบรรทัดคำสั่ง สิ่งนี้จะเพิ่มความซับซ้อนของรหัสที่จำเป็นในการแยกวิเคราะห์ตัวเลือก
สิ่งต่าง ๆ ยังคงซับซ้อนมากขึ้นหากบางตัวเลือกของคุณใช้อาร์กิวเมนต์ หรือที่เรียกว่า อาร์กิวเมนต์ option ตัวอย่างเช่น ตัวเลือก ls -w
(ความกว้าง) คาดว่าจะตามด้วยตัวเลข ซึ่งแสดงถึงความกว้างในการแสดงผลสูงสุดของเอาต์พุต และแน่นอน คุณอาจส่งพารามิเตอร์อื่นๆ ลงในสคริปต์ของคุณ ซึ่งเป็นเพียงค่าข้อมูล ซึ่งไม่ใช่ตัวเลือกเลย
โชคดี getopts
จัดการความซับซ้อนนี้ให้กับคุณ และเนื่องจากเป็นบิวด์อิน จึงสามารถใช้ได้กับทุกระบบที่มี Bash shell ดังนั้นจึงไม่ต้องติดตั้งอะไร
หมายเหตุ: getopt ไม่ใช่ getopt
มียูทิลิตี้เก่าที่เรียกว่า getopt
นี่เป็น โปรแกรม อรรถประโยชน์ขนาดเล็ก ไม่ใช่ในตัว มี getopt
เวอร์ชันต่างๆ มากมายที่มีพฤติกรรมต่างกัน ในขณะที่ getops
ในตัวเป็นไปตามหลักเกณฑ์ของ POSIX
พิมพ์ getopts
พิมพ์ getopt
เนื่องจาก getopt
ไม่ใช่ buildin จึงไม่แบ่งปันประโยชน์อัตโนมัติบางอย่างที่ getopts
มี เช่น การจัดการช่องว่างอย่างสมเหตุสมผล ด้วย getopts
เปลือก Bash กำลังเรียกใช้สคริปต์ของคุณและเปลือก Bash จะทำการแยกวิเคราะห์ตัวเลือก คุณไม่จำเป็นต้องเรียกใช้โปรแกรมภายนอกเพื่อจัดการการแยกวิเคราะห์
การประนีประนอมคือ getopts
ไม่รองรับชื่อตัวเลือกรูปแบบยาวสองเส้นประ ดังนั้นคุณสามารถใช้ตัวเลือกที่มีรูปแบบเช่น -w
แต่ไม่ใช่ ” ---wide-format
” ในทางกลับกัน หากคุณมีสคริปต์ที่ยอมรับตัวเลือก -a
, -b
และ -c
getopts
ให้คุณรวมพวกมันเข้าด้วยกัน เช่น -abc
, -bca
หรือ -bac
เป็นต้น
เรากำลังพูดถึงและสาธิต getopts
ในบทความนี้ ดังนั้นตรวจสอบให้แน่ใจว่าคุณเพิ่ม “s” สุดท้ายในชื่อคำสั่ง
ที่เกี่ยวข้อง: วิธี Escape Spaces ในเส้นทางไฟล์บน Command Line ของ Windows
สรุปโดยย่อ: การจัดการค่าพารามิเตอร์
สคริปต์นี้ไม่ใช้ตัวเลือกเส้นประ เช่น -a
หรือ -b
ยอมรับพารามิเตอร์ "ปกติ" ในบรรทัดคำสั่งและเข้าถึงได้ภายในสคริปต์เป็นค่า
#!/bin/bash #รับตัวแปรทีละตัว echo "ตัวแปรหนึ่ง: $1" echo "ตัวแปรที่สอง: $2" echo "ตัวแปรสาม: $3" # วนซ้ำตัวแปร สำหรับ var ใน " $@" do เสียงสะท้อน "$ var" เสร็จแล้ว
พารามิเตอร์สามารถเข้าถึงได้ภายในสคริปต์เป็นตัวแปร $1
, $2
หรือ $3
คัดลอกข้อความนี้ไปยังโปรแกรมแก้ไขและบันทึกเป็นไฟล์ชื่อ “variables.sh” เราจะต้องทำให้มันสามารถเรียกใช้งานได้ด้วยคำสั่ง chmod
คุณจะต้องทำตามขั้นตอนนี้สำหรับสคริปต์ทั้งหมดที่เราพูดถึง เพียงแทนที่ชื่อของไฟล์สคริปต์ที่เหมาะสมในแต่ละครั้ง
chmod +x ตัวแปร.sh
หากเรารันสคริปต์โดยไม่มีพารามิเตอร์ เราก็จะได้ผลลัพธ์นี้
./variables.sh
เราไม่ได้ส่งผ่านพารามิเตอร์ใดๆ ดังนั้นสคริปต์จึงไม่มีค่าที่จะรายงาน คราวนี้ให้พารามิเตอร์บางอย่าง
./variables.sh วิธีเกินบรรยาย
ตามที่คาดไว้ ตัวแปร $1
, $2
และ $3
ถูกตั้งค่าเป็นค่าพารามิเตอร์แล้ว และเราจะเห็นว่าสิ่งเหล่านี้ถูกพิมพ์ออกมา
การจัดการพารามิเตอร์แบบตัวต่อตัวประเภทนี้หมายความว่าเราจำเป็นต้องทราบล่วงหน้าว่าจะมีพารามิเตอร์กี่ตัว ลูปที่ด้านล่างของสคริปต์ไม่สนใจว่าจะมีพารามิเตอร์กี่ตัว แต่จะวนซ้ำทั้งหมดเสมอ
หากเราจัดเตรียมพารามิเตอร์ที่สี่ พารามิเตอร์นั้นจะไม่ถูกกำหนดให้กับตัวแปร แต่ลูปยังคงจัดการพารามิเตอร์นั้น
./variables.sh วิธี geek เว็บไซต์
หากเราใส่เครื่องหมายคำพูดรอบคำสองคำ จะถือว่าเป็นพารามิเตอร์เดียว
./variables.sh วิธี "เกินบรรยาย"
หากเราต้องการให้สคริปต์ของเราจัดการกับตัวเลือกทั้งหมด ตัวเลือกที่มีอาร์กิวเมนต์ และพารามิเตอร์ประเภทข้อมูล "ปกติ" เราจะต้องแยกตัวเลือกออกจากพารามิเตอร์ปกติ เราสามารถทำได้โดยวางตัวเลือกทั้งหมด—มีหรือไม่มีอาร์กิวเมนต์— ก่อน พารามิเตอร์ปกติ
แต่อย่าวิ่งก่อนที่เราจะเดินได้ ลองดูกรณีที่ง่ายที่สุดสำหรับการจัดการตัวเลือกบรรทัดคำสั่ง
ตัวเลือกการจัดการ
เราใช้ getopts
ในลูป while
การวนซ้ำแต่ละครั้งจะทำงานบนตัวเลือกเดียวที่ส่งผ่านไปยังสคริปต์ ในแต่ละกรณี ตัวแปร OPTION
จะถูกตั้งค่าเป็นตัวเลือกที่ระบุโดย getopts
ในการวนซ้ำแต่ละครั้ง getopts
จะย้ายไปที่ตัวเลือกถัดไป เมื่อไม่มีตัวเลือกเพิ่มเติม getopts
จะส่งกลับค่า false
และวง while
จะออก
ตัวแปร OPTION
จะจับคู่กับรูปแบบในแต่ละประโยคคำสั่ง case เนื่องจากเราใช้คำสั่ง case ไม่สำคัญว่ามีตัวเลือกใดในบรรทัดรับคำสั่ง แต่ละตัวเลือกจะถูกทิ้งลงในคำสั่ง case และส่วนคำสั่งที่เหมาะสมจะถูกเรียกใช้
ส่วนย่อยแต่ละรายการในคำสั่ง case ทำให้ง่ายต่อการดำเนินการเฉพาะตัวเลือกภายในสคริปต์ โดยปกติ ในสคริปต์ที่ใช้งานจริง คุณจะต้องตั้งค่าตัวแปรในแต่ละประโยค และสิ่งเหล่านี้จะทำหน้าที่เป็นแฟล็กเพิ่มเติมในสคริปต์ อนุญาตหรือปฏิเสธฟังก์ชันการทำงานบางอย่าง
คัดลอกข้อความนี้ลงในโปรแกรมแก้ไขและบันทึกเป็นสคริปต์ชื่อ “options.sh” และทำให้ใช้งานได้
#!/bin/bash ในขณะที่รับตัวเลือก 'abc'; ทำ กรณี "$OPTION" ใน ก) echo "ตัวเลือกที่ใช้" ;; ข) echo "ตัวเลือก b ที่ใช้" ;; ค) echo "ตัวเลือก c ที่ใช้" ;; ?) echo "การใช้งาน: $(basename $0) [-a] [-b] [-c]" ทางออก 1 ;; esac เสร็จแล้ว
นี่คือบรรทัดที่กำหนดลูป while

ในขณะที่รับตัวเลือก 'abc'; ทำ
คำสั่ง getopts
ตามด้วย สตริงตัวเลือก รายการนี้จะแสดงรายการตัวอักษรที่เราจะใช้เป็นตัวเลือก ใช้ได้เฉพาะตัวอักษรในรายการนี้เป็นตัวเลือก ดังนั้นในกรณีนี้ -d
จะไม่ถูกต้อง สิ่งนี้จะถูกดักโดยคำสั่ง ?)
เนื่องจาก getopts
ส่งคืนเครื่องหมายคำถาม “ ?
” สำหรับตัวเลือกที่ไม่ระบุ หากเป็นเช่นนั้น ระบบจะพิมพ์การใช้งานที่ถูกต้องไปที่หน้าต่างเทอร์มินัล:
echo "การใช้งาน: $(basename $0) [-a] [-b] [-c]"
ตามธรรมเนียม การใส่ตัวเลือกในวงเล็บ “ []
” ในข้อความการใช้งานที่ถูกต้องประเภทนี้หมายความว่าตัวเลือกนั้นเป็นทางเลือก คำสั่ง basename จะดึงพาธไดเร็กทอรีออกจากชื่อไฟล์ ชื่อไฟล์สคริปต์อยู่ใน $0
ในสคริปต์ทุบตี
ลองใช้สคริปต์นี้กับชุดบรรทัดคำสั่งต่างๆ
./options.sh -a
./options.sh -a -b -c
./options.sh -ab -c
./options.sh -cab
ดังที่เราเห็น ชุดค่าผสมการทดสอบทั้งหมดของเรามีการแยกวิเคราะห์และจัดการอย่างถูกต้อง จะเกิดอะไรขึ้นหากเราลองใช้ตัวเลือกที่ไม่มีอยู่จริง
./options.sh -d
ประโยคการใช้งานถูกเรียกใช้ ซึ่งเป็นสิ่งที่ดี แต่เรายังได้รับข้อความแสดงข้อผิดพลาดจากเชลล์ด้วย นั่นอาจจะใช่หรือไม่สำคัญกับกรณีการใช้งานของคุณ หากคุณกำลังเรียกใช้สคริปต์จากสคริปต์อื่นที่ต้องแยกวิเคราะห์ข้อความแสดงข้อผิดพลาด จะทำให้ยากขึ้นหากเชลล์สร้างข้อความแสดงข้อผิดพลาดด้วย
การปิดข้อความแสดงข้อผิดพลาดของเชลล์นั้นง่ายมาก สิ่งที่เราต้องทำคือใส่เครื่องหมายโคลอน ” :
” เป็นอักขระตัวแรกของสตริงตัวเลือก
แก้ไขไฟล์ "options.sh" และเพิ่มโคลอนเป็นอักขระตัวแรกของสตริงตัวเลือก หรือบันทึกสคริปต์นี้เป็น "options2.sh" และทำให้สามารถเรียกใช้งานได้
#!/bin/bash ในขณะที่ getopts ':abc' OPTION; ทำ กรณี "$OPTION" ใน ก) echo "ตัวเลือกที่ใช้" ;; ข) echo "ตัวเลือก b ที่ใช้" ;; ค) echo "ตัวเลือก c ที่ใช้" ;; ?) echo "การใช้งาน: $(basename $0) [-a] [-b] [-c]" ทางออก 1 ;; esac เสร็จแล้ว
เมื่อเราเรียกใช้สิ่งนี้และสร้างข้อผิดพลาด เราได้รับข้อความแสดงข้อผิดพลาดของเราเองโดยไม่มีข้อความเชลล์ใดๆ
./options2.sh.sh -d
การใช้ getopts กับอาร์กิวเมนต์ตัวเลือก
หากต้องการบอก getopts
ว่าตัวเลือกจะตามมาด้วยอาร์กิวเมนต์ ให้ใส่เครื่องหมายทวิภาค ” :
” ไว้ด้านหลังตัวอักษร option ในสตริง options ทันที
ถ้าเราทำตาม "b" และ "c" ในสตริงตัวเลือกของเราด้วยเครื่องหมายทวิภาค getopt
จะคาดหวังอาร์กิวเมนต์สำหรับตัวเลือกเหล่านี้ คัดลอกสคริปต์นี้ลงในโปรแกรมแก้ไขของคุณและบันทึกเป็น “arguments.sh” และทำให้สามารถเรียกใช้งานได้
โปรดจำไว้ว่า เครื่องหมายโคลอน แรก ในสตริงตัวเลือกใช้เพื่อระงับข้อความแสดงข้อผิดพลาดของเชลล์ ซึ่งไม่เกี่ยวข้องกับการประมวลผลอาร์กิวเมนต์
เมื่อ getopt
ประมวลผลตัวเลือกด้วยอาร์กิวเมนต์ อาร์กิวเมนต์จะถูกวางไว้ในตัวแปร OPTARG
หากคุณต้องการใช้ค่านี้ในที่อื่นในสคริปต์ของคุณ คุณจะต้องคัดลอกไปยังตัวแปรอื่น
#!/bin/bash ในขณะที่ getopts ':ab:c:' OPTION; ทำ กรณี "$OPTION" ใน ก) echo "ตัวเลือกที่ใช้" ;; ข) argB="$OPTARG" echo "ตัวเลือก b ใช้กับ: $argB" ;; ค) argC="$OPTARG" echo "ตัวเลือก c ที่ใช้กับ: $argC" ;; ?) echo "การใช้งาน: $(basename $0) [-a] [-b อาร์กิวเมนต์] [-c อาร์กิวเมนต์]" ทางออก 1 ;; esac เสร็จแล้ว
ลองเรียกใช้และดูว่ามันทำงานอย่างไร
./arguments.sh -a -b "วิธีการเกินบรรยาย" -c reviewgeek
./arguments.sh -c reviewgeek -a
ดังนั้น ตอนนี้ เราสามารถจัดการกับตัวเลือกที่มีหรือไม่มีอาร์กิวเมนต์ โดยไม่คำนึงถึงลำดับของตัวเลือกที่ได้รับในบรรทัดคำสั่ง
แต่แล้วพารามิเตอร์ปกติล่ะ? เราพูดไปก่อนหน้านี้แล้วว่าเรารู้ว่าเราต้องใส่สิ่งเหล่านั้นในบรรทัดคำสั่งหลังจากตัวเลือกใดๆ มาดูกันว่าจะเกิดอะไรขึ้นถ้าเราทำ
การผสมตัวเลือกและพารามิเตอร์
เราจะเปลี่ยนสคริปต์ก่อนหน้าของเราให้รวมอีกหนึ่งบรรทัด เมื่อออกจากลูป while
และจัดการตัวเลือกทั้งหมดแล้ว เราจะพยายามเข้าถึงพารามิเตอร์ปกติ เราจะพิมพ์ค่าออกมาเป็น $1
บันทึกสคริปต์นี้เป็น “arguments2.sh” และทำให้สามารถเรียกใช้งานได้
#!/bin/bash ในขณะที่ getopts ':ab:c:' OPTION; ทำ กรณี "$OPTION" ใน ก) echo "ตัวเลือกที่ใช้" ;; ข) argB="$OPTARG" echo "ตัวเลือก b ใช้กับ: $argB" ;; ค) argC="$OPTARG" echo "ตัวเลือก c ที่ใช้กับ: $argC" ;; ?) echo "การใช้งาน: $(basename $0) [-a] [-b อาร์กิวเมนต์] [-c อาร์กิวเมนต์]" ทางออก 1 ;; esac เสร็จแล้ว echo "ตัวแปรหนึ่งคือ: $1"
ตอนนี้เราจะลองใช้ตัวเลือกและพารามิเตอร์ร่วมกันสองสามอย่าง
./arguments2.sh dave
./arguments2.sh -a dave
./arguments2.sh -a -c how-to-geek dave
ตอนนี้เราก็เห็นปัญหาแล้ว ทันทีที่ใช้ตัวเลือกใดๆ ตัวแปร $1
เป็นต้นไปจะถูกเติมด้วยแฟล็กตัวเลือกและอาร์กิวเมนต์ ในตัวอย่างที่แล้ว $4
จะเก็บค่าพารามิเตอร์ “dave” แต่คุณจะเข้าถึงค่านั้นในสคริปต์ของคุณได้อย่างไร ถ้าคุณไม่รู้ว่าจะใช้ตัวเลือกและข้อโต้แย้งจำนวนเท่าใด
คำตอบคือใช้ OPTIND
และคำสั่ง shift
คำสั่ง shift
จะละทิ้งพารามิเตอร์แรกโดยไม่คำนึงถึงประเภทจากรายการพารามิเตอร์ พารามิเตอร์อื่น "สับเปลี่ยน" ดังนั้นพารามิเตอร์ 2 จะกลายเป็นพารามิเตอร์ 1 พารามิเตอร์ 3 กลายเป็นพารามิเตอร์ 2 เป็นต้น ดังนั้น $2
กลายเป็น $1
, $3
กลายเป็น $2
และอื่นๆ
หากคุณระบุ shift
ด้วยตัวเลข ระบบจะนำพารามิเตอร์จำนวนมากนั้นออกจากรายการ
OPTIND
นับตัวเลือกและอาร์กิวเมนต์ตามที่พบและประมวลผล เมื่อตัวเลือกและอาร์กิวเมนต์ทั้งหมดได้รับการประมวลผล OPTIND
จะสูงกว่าจำนวนตัวเลือกหนึ่งตัว ดังนั้น หากเราใช้พารามิเตอร์ shift to trim (OPTIND-1)
จากรายการพารามิเตอร์ เราจะเหลือพารามิเตอร์ปกติเป็น $1
เป็นต้นไป
นั่นคือสิ่งที่สคริปต์นี้ทำ บันทึกสคริปต์นี้เป็น “arguments3.sh” และทำให้สามารถเรียกใช้งานได้
#!/bin/bash ในขณะที่ getopts ':ab:c:' OPTION; ทำ กรณี "$OPTION" ใน ก) echo "ตัวเลือกที่ใช้" ;; ข) argB="$OPTARG" echo "ตัวเลือก b ใช้กับ: $argB" ;; ค) argC="$OPTARG" echo "ตัวเลือก c ที่ใช้กับ: $argC" ;; ?) echo "การใช้งาน: $(basename $0) [-a] [-b อาร์กิวเมนต์] [-c อาร์กิวเมนต์]" ทางออก 1 ;; esac เสร็จแล้ว echo "ก่อน - ตัวแปรหนึ่งคือ: $1" กะ "$(($OPTIND -1))" echo "หลัง - ตัวแปรหนึ่งคือ: $1" echo "อาร์กิวเมนต์ที่เหลือ (ตัวถูกดำเนินการ)" สำหรับ x ใน "$@" ทำ เสียงสะท้อน $x เสร็จแล้ว
เราจะดำเนินการนี้ด้วยตัวเลือก อาร์กิวเมนต์ และพารามิเตอร์ผสมกัน
./arguments3.sh -a -c How-to-geek "dave dee" จงอยงี่เง่า mick tich
เราสามารถเห็นได้ว่าก่อนที่เราจะเรียก shift
นั้น $1
ถือ “-a” แต่หลังจากที่คำสั่ง shift $1
ถือพารามิเตอร์ non-option และ non-argument ตัวแรกของเรา เราสามารถวนซ้ำพารามิเตอร์ทั้งหมดได้อย่างง่ายดายเหมือนที่เราทำในสคริปต์โดยไม่มีการแยกวิเคราะห์ตัวเลือก
เป็นตัวเลือกที่ดีเสมอ
การจัดการตัวเลือกและข้อโต้แย้งในสคริปต์ไม่จำเป็นต้องซับซ้อน ด้วย getopts
คุณสามารถสร้างสคริปต์ที่จัดการตัวเลือกบรรทัดคำสั่ง อาร์กิวเมนต์ และพารามิเตอร์เหมือนกับที่สคริปต์เนทีฟที่สอดคล้องกับ POSIX ควรมี