getopt, getopts veya manuel ayrıştırma - hem kısa hem de uzun seçenekleri desteklemek istediğimde ne kullanmalıyım?


32

Şu anda aşağıdaki gereksinimleri olan bir Bash betiği yazıyorum:

  • Çok çeşitli Unix / Linux platformlarında çalışmalıdır.
  • hem kısa hem de (GNU) uzun seçenekleri desteklemesi gerekir

getoptsTaşınabilirlik açısından tercih edilen yol olacağını biliyorum, ancak AFAIK uzun seçenekleri desteklemiyor.

getoptuzun seçenekleri destekler, ancak BashGuide buna karşı şiddetle tavsiye eder:

Asla getopt (1) kullanmayın. getopt, boş argüman dizeleri ya da gömülü beyaz boşluklu argümanları işleyemez. Lütfen onun var olduğunu unut.

Yani, hala manuel ayrıştırma seçeneği var. Bu hataya açık, oldukça fazla kazan kodu üretiyor ve hataları kendi başıma halletmem gerekiyor (sanırım getopt(s)kendi başlarına hata yönetimi yapıyoruz).

Peki, bu durumda tercih edilen seçim ne olurdu?


Yanıtlar:


9

Bir dizi Unices için taşınabilir olması gerekiyorsa, POSIX sh ürününe bağlı kalmanız gerekir. Ve AFAIU orada başka seçeneğiniz yok, ancak el ile ele almakla başa çıkmak.


25

getoptvs getoptsdini bir mesele gibi görünüyor. Karşı argümanlar gelince getoptde Bash SSS :

  • " getoptboş argüman dizeleriyle başa çıkamaz" ifadesi , hiç desteklemiyor gibi görünen ve en azından Bash 4.2.24 için okumadan , isteğe bağlı argümanlarla bilinen bir konuya atıfta bulunur . Kimden :getoptshelp getoptsman getopt

    getopt (3), boş bir isteğe bağlı argüman verilen isteğe bağlı argümanlarla uzun seçenekleri ayrıştırabilir (ancak bunu kısa seçenekler için yapamaz). Bu getopt (1) boş olan isteğe bağlı argümanları yokmuş gibi ele alır.

" getopt[[]] Gömülü boşluklu argümanlarla başa çıkamaz" ifadesinin nereden geldiğini bilmiyorum, ama deneyelim:

  • 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
    
  • koşmak:

    $ ./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
    

Bana bak ve eşleş gibi görünüyorsun, ama birisinin cümleyi nasıl yanlış anladığımı göstereceğine eminim. Tabii ki taşınabilirlik sorunu hala duruyor; Daha eski bir Bash olan veya hiç olmayan platformlara ne kadar zaman harcayacağınıza karar vermeniz gerekir. Kendi ipucum YAGNI ve KISS kurallarını kullanmaktır - Sadece kullanılacağını bildiğiniz özel platformlar için geliştirin. Kabuk kodu taşınabilirliği, geliştirme süresi sonsuzluğa gittiği için genellikle% 100'dür.


11
OP getopt, burada alıntı yaptığınız sırada Linux'a özelken , birçok Unix platformuna taşınabilir olma ihtiyacından bahsetti . Bunun bir getoptparçası olmadığını bash, bir GNU yardımcı programının bile olmadığını ve Linux'ta util-linux paketiyle birlikte gönderildiğini unutmayın.
Stéphane Chazelas

2
Çoğu platformda getopt, yalnızca Linux AFAIK, uzun seçenekleri veya argümanlardaki boşlukları destekleyen bir tane ile gelir. Diğerleri sadece System V sözdizimini destekleyecektir.
Stéphane Chazelas

7
getoptLinux piyasaya sürülmeden çok önce V Sisteminden gelen geleneksel bir emirdir. getoptasla standartlaştırılmadı. POSIX, Unix veya Linux (LSB) hiçbiri getoptkomutu standart hale getirmedi. getoptsHer üçünde de belirtilmiş, ancak uzun seçenekler için desteksiz
Stéphane Chazelas

1
Sadece bu tartışmaya eklemek için: bu geleneksel değildir getopt. @ StéphaneChazelas tarafından belirtildiği gibi linux-utils aromasıdır. Yukarıda açıklanan sözdizimini devre dışı bırakacak olan eski seçeneklere sahiptir, özellikle "GETOPT_COMPATIBLE manto" durumları getopt'u SYNOPSIS'te belirtilen ilk çağrı biçimini kullanmaya zorlar "durumlarını gösterir. Bununla birlikte, hedef sistemlerin bu paketi
kurmasını beklerseniz

1
OP’nin "getopt’un geleneksel sürümleri boş argüman dizelerini veya gömülü beyaz boşluklu argümanları işleyemez." ve getopt'un Linux-Linux versiyonunun kullanılmaması gerektiğine dair hiçbir kanıt yoktur ve artık doğru değildir. Hızlı bir anket (5+ yıl sonra), ArchLinux, SUSE, Ubuntu, RedHat, Fedora ve CentOS'taki varsayılan getopt sürümünün (ve çoğu türev varyantının), isteğe bağlı argümanları ve beyaz boşluklu argümanları desteklediğini göstermektedir.
mtalexan

10

Bu getopts_long , betiğinizin içine yerleştirebileceğiniz bir POSIX kabuğu işlevi olarak yazılmış.

Linux'un getopt(dan util-linux) geleneksel modda olmadığında doğru çalıştığını ve uzun seçenekleri desteklediğini, ancak diğer Unices için taşınabilir olmanız gerektiğinde muhtemelen sizin için bir seçenek olmadığını unutmayın.

Ksh93 ( getopts) ve zsh ( zparseopts) ' nin son sürümleri, çoğu Unice için kullanılabildiği için sizin için bir seçenek olabilecek uzun seçeneklerin ayrıştırılması için yerleşik desteğe sahiptir (çoğu zaman varsayılan olarak kurulmaz).

Diğer bir seçenek, bugünlerde çoğu Unice'de mevcut olması gereken modülü kullanmaktır perlve Getopt::Longtüm senaryoyu yazarak ya perlda sadece seçeneği ayrıştırmak ve çıkarılan bilgileri kabuğa beslemek için perl komutunu çağırmak yeterlidir. Gibi bir şey:

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::LongNe yapabileceğini ve diğer seçenek ayrıştırıcılardan ne kadar farklı olduğunu görün .


2

Bu konuyla ilgili her tartışma, ayrıştırma kodunu elle yazma seçeneğini vurgulamaktadır - ancak o zaman işlevsellik ve taşınabilirlikten emin olabilirsiniz. Kullanımı kolay açık kaynaklı kod üreteçleri tarafından üretebileceğiniz ve yeniden oluşturduğunuz kodları yazmamanızı tavsiye ederim. Sorunuza kesin bir cevap vermek için tasarlanmış Argbash kullanın . Bu bir olduğunu iyi belgelenmiş bir şekilde mevcut kod üreteci komut satırı uygulaması , çevrimiçi ya da olarak Docker görüntüsü .

Bazıları kullanamayan bash kütüphanelerine karşı tavsiyelerde bulunuyorum getopt(ki bunlar oldukça avantajsız kılıyor) ve betiğinizle devasa ve okunamayan bir kabuk bloğu bir araya getirmek acı verici.


0

Onu getoptdestekleyen sistemlerde kullanabilir ve desteklemeyen sistemlerde geri dönüş kullanabilirsiniz.

Mesela pure-getopt, saf Bash'de GNU'nun yerine geçmesi için uygulanmaktadır getopt.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.