getopt, getopts หรือการแยกวิเคราะห์แบบแมนนวล - จะต้องใช้อะไรเมื่อฉันต้องการสนับสนุนตัวเลือกทั้งระยะสั้นและระยะยาว


32

ขณะนี้ฉันกำลังเขียนสคริปต์ Bash ซึ่งมีข้อกำหนดดังต่อไปนี้:

  • ควรรันบนแพลตฟอร์ม Unix / Linux ที่หลากหลาย
  • ควรสนับสนุนตัวเลือกทั้งแบบสั้นและยาว (GNU)

ฉันรู้ว่าgetoptsจะเป็นวิธีที่ต้องการในแง่ของการพกพา แต่ AFAIK มันไม่สนับสนุนตัวเลือกยาว

getoptรองรับตัวเลือกยาว แต่BashGuideแนะนำอย่างยิ่งกับมัน:

ห้ามใช้ getopt (1) getopt ไม่สามารถจัดการกับสตริงอาร์กิวเมนต์ว่างเปล่าหรือขัดแย้งกับช่องว่างที่ฝังตัว โปรดลืมว่ามันมีอยู่จริง

ดังนั้นยังคงมีตัวเลือกในการแยกวิเคราะห์ด้วยตนเอง นี่เป็นข้อผิดพลาดง่ายสร้างรหัสสำเร็จรูปค่อนข้างมากและฉันต้องจัดการข้อผิดพลาดด้วยตัวเอง (ฉันเดาว่าgetopt(s)จัดการข้อผิดพลาดด้วยตัวเอง)

ดังนั้นสิ่งที่จะเป็นตัวเลือกที่ต้องการในกรณีนี้


คำตอบ:


9

หากจะต้องพกพาไปที่ช่วงของ Unices คุณจะต้องติดตั้ง POSIX sh และ AFAIU ที่นั่นคุณไม่มีทางเลือกนอกจากจะจัดการกับการโต้เถียงด้วยมือ


25

getoptvs getoptsดูเหมือนว่าจะเป็นปัญหาทางศาสนา สำหรับการโต้แย้งgetoptในBash FAQ :

  • " getoptไม่สามารถจัดการกับสตริงอาร์กิวเมนต์ว่างเปล่า" ดูเหมือนว่าจะอ้างถึงปัญหาที่ทราบด้วยอาร์กิวเมนต์ที่เป็นตัวเลือกซึ่งดูเหมือนว่าgetoptsไม่รองรับเลย (อย่างน้อยจากการอ่านhelp getoptsBash 4.2.24) จากman getopt:

    getopt (3) สามารถแยกวิเคราะห์ตัวเลือกแบบยาวด้วยอาร์กิวเมนต์ที่เป็นตัวเลือกที่ได้รับอาร์กิวเมนต์ตัวเลือกที่ว่างเปล่า (แต่ไม่สามารถทำได้สำหรับตัวเลือกแบบสั้น) getopt นี้ (1) ใช้อาร์กิวเมนต์ที่เป็นทางเลือกที่ว่างเปล่าราวกับว่ามันไม่มีอยู่

ฉันไม่ทราบว่า " getoptไม่สามารถจัดการกับข้อโต้แย้ง [... ] ด้วยช่องว่างที่ฝังตัว" มาจากไหน แต่ลองทดสอบกัน:

  • test.sh:

    #!/usr/bin/env bash
    set -o errexit -o noclobber -o nounset -o pipefail
    params="$(getopt -o ab:c -l alpha,bravo:,charlie --name "$0" -- "$@")"
    eval set -- "$params"
    
    while true
    do
        case "$1" in
            -a|--alpha)
                echo alpha
                shift
                ;;
            -b|--bravo)
                echo "bravo=$2"
                shift 2
                ;;
            -c|--charlie)
                echo charlie
                shift
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Not implemented: $1" >&2
                exit 1
                ;;
        esac
    done
    
  • วิ่ง:

    $ ./test.sh -
    $ ./test.sh -acb '   whitespace   FTW   '
    alpha
    charlie
    bravo=   whitespace   FTW   
    $ ./test.sh -ab '' -c
    alpha
    bravo=
    charlie
    $ ./test.sh --alpha --bravo '   whitespace   FTW   ' --charlie
    alpha
    bravo=   whitespace   FTW   
    charlie
    

ดูเหมือนจะตรวจสอบและผสมพันธุ์กับฉัน แต่ฉันแน่ใจว่ามีบางคนจะแสดงให้เห็นว่าฉันเข้าใจผิดประโยคอย่างสมบูรณ์ แน่นอนปัญหาการพกพายังคงมีอยู่ คุณจะต้องตัดสินใจว่าควรใช้เวลาเท่าไรในการลงทุนในแพลตฟอร์มที่มีรุ่นเก่ากว่าหรือไม่มี Bash เคล็ดลับของฉันคือการใช้แนวทางYAGNIและKISS - พัฒนาเฉพาะสำหรับแพลตฟอร์มเฉพาะที่คุณรู้ว่าจะใช้ ความสามารถในการพกพาของรหัสเชลล์โดยทั่วไปนั้นจะอยู่ที่ 100% เนื่องจากเวลาในการพัฒนาจะไม่สิ้นสุด


11
OP กล่าวถึงความจำเป็นที่จะต้องพกพาไปยังแพลตฟอร์ม Unix จำนวนมากในขณะที่getoptคุณอ้างถึงที่นี่เป็นเฉพาะ Linux โปรดทราบว่าgetoptมันไม่ได้เป็นส่วนหนึ่งbashมันไม่ใช่แม้แต่ยูทิลิตี้ GNU และบน Linux ก็มาพร้อมกับแพคเกจ util-linux
Stéphane Chazelas

2
แพลตฟอร์มส่วนใหญ่มีgetoptเฉพาะ Linux AFAIK ที่มาพร้อมกับตัวเลือกที่รองรับตัวเลือกยาวหรือช่องว่างในการโต้แย้ง อีกอันจะรองรับไวยากรณ์ System V เท่านั้น
Stéphane Chazelas

7
getoptเป็นคำสั่งดั้งเดิมที่มาจาก System V มานานก่อนที่ Linux จะวางจำหน่าย getoptไม่เคยเป็นมาตรฐาน POSIX, Unix หรือ Linux (LSB) ไม่มีgetoptคำสั่งที่เป็นมาตรฐาน getoptsถูกระบุในทั้งสาม แต่ไม่สนับสนุนตัวเลือกแบบยาว
Stéphane Chazelas

1
เพียงเพื่อเพิ่มการสนทนานี้: getoptนี้ไม่ได้เป็นแบบดั้งเดิม มันเป็นรสชาติของ linux-utils ตามที่ระบุโดย @ StéphaneChazelas มันมีตัวเลือกแบบดั้งเดิมที่จะปิดการใช้งานไวยากรณ์ที่อธิบายข้างต้นโดยเฉพาะอย่างยิ่งสถานะ manpage "GETOPT_COMPATIBLE บังคับให้ getopt ใช้รูปแบบการโทรครั้งแรกตามที่ระบุไว้ใน SYNOPSIS" อย่างไรก็ตามหากคุณสามารถคาดหวังว่าระบบเป้าหมายจะติดตั้งแพคเกจนี้จะเป็นไปโดยสิ้นเชิงเนื่องจาก getopt ดั้งเดิมนั้นแย่มากและ getopt ของ Bash นั้นมี จำกัด มาก
n.caillou

1
ลิงก์ของ OP ที่อ้างว่า "getopt เวอร์ชันดั้งเดิมไม่สามารถจัดการกับสตริงอาร์กิวเมนต์ว่างเปล่าหรืออาร์กิวเมนต์ที่มีช่องว่างฝังตัวอยู่" และไม่ควรใช้ getopt เวอร์ชัน util-linux ไม่มีหลักฐานและไม่แม่นยำอีกต่อไป การสำรวจอย่างรวดเร็ว (5+ ปีต่อมา) แสดงให้เห็นว่า getopt รุ่นเริ่มต้นมีให้บริการตั้งแต่ ArchLinux, SUSE, Ubuntu, RedHat, Fedora และ CentOS (รวมถึงตัวแปรอนุพันธ์ส่วนใหญ่) ทั้งหมดสนับสนุนอาร์กิวเมนต์และอาร์กิวเมนต์ที่มีตัวเลือกพร้อมช่องว่าง
mtalexan

10

getopts_longนี้เขียนเป็นฟังก์ชันเชลล์ POSIX ที่คุณอาจฝังไว้ในสคริปต์ของคุณ

โปรดทราบว่า Linux getopt(จากutil-linux) ทำงานอย่างถูกต้องเมื่อไม่ได้อยู่ในโหมดดั้งเดิมและรองรับตัวเลือกแบบยาว แต่อาจไม่ใช่ตัวเลือกสำหรับคุณถ้าคุณต้องพกพาไปที่ Unices อื่น ๆ

รุ่นล่าสุดของ ksh93 ( getopts) และ zsh ( zparseopts) มีการสนับสนุนในตัวสำหรับการแยกวิเคราะห์ตัวเลือกแบบยาวซึ่งอาจเป็นตัวเลือกสำหรับคุณเนื่องจากมีอยู่ใน Unices ส่วนใหญ่ (แม้ว่าจะไม่ได้ติดตั้งตามค่าเริ่มต้น)

อีกทางเลือกหนึ่งคือการใช้งานperlและGetopt::Longโมดูลของทั้งคู่ซึ่งควรมีอยู่ใน Unices ส่วนใหญ่ในปัจจุบันไม่ว่าจะโดยการเขียนสคริปต์ทั้งหมดperlหรือเพียงแค่เรียก perl เพียงเพื่อแยกวิเคราะห์ตัวเลือกและดึงข้อมูลที่แตกออกมาไปยังเชลล์ สิ่งที่ต้องการ:

parsed_ops=$(
  perl -MGetopt::Long -le '

    @options = (
      "foo=s", "bar", "neg!"
    );

    Getopt::Long::Configure "bundling";
    $q="'\''";
    GetOptions(@options) or exit 1;
    for (map /(\w+)/, @options) {
      eval "\$o=\$opt_$_";
      $o =~ s/$q/$q\\$q$q/g;
      print "opt_$_=$q$o$q"
    }' -- "$@"
) || exit
eval "$parsed_ops"
# and then use $opt_foo, $opt_bar...

ดูperldoc Getopt::Longว่ามันสามารถทำอะไรและแตกต่างจากตัวแยกวิเคราะห์ตัวเลือกอื่น ๆ อย่างไร


2

ทุกการอภิปรายเกี่ยวกับเรื่องนี้เน้นตัวเลือกในการเขียนรหัสการแยกวิเคราะห์ด้วยตนเอง - จากนั้นคุณจึงมั่นใจได้เกี่ยวกับการทำงานและการพกพา ผมแนะนำให้คุณอย่าเขียนโค้ดที่คุณสามารถสร้างและสร้างขึ้นใหม่ได้โดยผู้สร้างโค้ดโอเพนซอร์ซที่ใช้งานง่าย ใช้Argbashซึ่งได้รับการออกแบบมาเพื่อให้คำตอบที่ชัดเจนสำหรับปัญหาของคุณ มันเป็นเอกสารที่ดีพอสร้างโค้ดใช้ได้เป็นแอปพลิเคบรรทัดคำสั่ง , ออนไลน์หรือเป็นภาพเทียบท่า

ฉันแนะนำให้ใช้กับไลบรารีทุบตีบางคนใช้getopt(ซึ่งทำให้ค่อนข้างไม่สามารถอธิบายได้) และมันเป็นความเจ็บปวดที่จะรวมกลุ่มหยดขนาดใหญ่ที่อ่านไม่ได้ด้วยสคริปต์ของคุณ


0

คุณสามารถใช้getoptกับระบบที่รองรับและใช้ระบบที่ไม่รองรับ

ยกตัวอย่างเช่น pure-getoptจะดำเนินการในทุบตีบริสุทธิ์จะเป็นดรอปแทนสำหรับ getoptGNU

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