getopt, getopts ou analyse manuelle - que dois-je utiliser lorsque je veux prendre en charge les options courtes et longues?

32

Actuellement, j'écris un script Bash qui a les exigences suivantes:

  • il devrait fonctionner sur une grande variété de plates-formes Unix / Linux
  • il devrait prendre en charge les options courtes et (GNU) longues

Je sais que ce getoptsserait le moyen préféré en termes de portabilité mais AFAIK ne prend pas en charge les options longues.

getoptprend en charge les options longues mais le BashGuide le déconseille fortement:

N'utilisez jamais getopt (1). getopt ne peut pas gérer des chaînes d'arguments vides ou des arguments avec des espaces blancs intégrés. Veuillez oublier qu'il a jamais existé.

Donc, il y a toujours la possibilité d'une analyse manuelle. Ceci est sujet aux erreurs, produit un peu de code passe-partout, et j'ai besoin de gérer les erreurs par moi-même (je suppose getopt(s)que le traitement des erreurs par eux-mêmes).

Alors, quel serait le choix préféré dans ce cas?

méthode d'aide
la source
connexes: stackoverflow.com/questions/192249/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Réponses:

9

Si elle doit être portable sur une gamme d'unités, vous devez vous en tenir à POSIX sh. Et AFAIU là, vous n'avez tout simplement pas d'autre choix que de rouler la gestion des arguments à la main.

vonbrand
la source
25

getoptvs getoptssemble être un problème religieux. Quant aux arguments contre getoptdans la FAQ Bash :

  • " getoptne peut pas gérer les chaînes d'arguments vides" semble faire référence à un problème connu avec des arguments facultatifs , qui ne semble getoptspas du tout prendre en charge (du moins d'après la lecture help getoptsde Bash 4.2.24). De man getopt:

    getopt (3) peut analyser les options longues avec des arguments facultatifs auxquels un argument facultatif vide est donné (mais ne peut pas le faire pour les options courtes). Cette getopt (1) traite les arguments facultatifs qui sont vides comme s'ils n'étaient pas présents.

Je ne sais pas d'où vient le " getoptne peut pas gérer les [...] arguments avec des espaces intégrés", mais testons-le:

  • 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
  • courir:

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

On dirait chèque et compagnon pour moi, mais je suis sûr que quelqu'un va montrer comment j'ai complètement mal compris la phrase. Bien sûr, le problème de la portabilité persiste; vous devrez décider combien de temps vaut la peine d'investir dans des plateformes avec un Bash plus ancien ou aucun disponible. Ma propre astuce consiste à utiliser les directives YAGNI et KISS - Développez uniquement pour les plates-formes spécifiques que vous savez que vous allez utiliser. La portabilité du code shell atteint généralement 100% lorsque le temps de développement passe à l'infini.

l0b0
la source
11
L'OP a mentionné la nécessité d'être portable sur de nombreuses plates-formes Unix alors que ce getoptque vous citez ici est spécifique à Linux. Notez que cela getoptne fait pas partie bash, ce n'est même pas un utilitaire GNU et sous Linux est livré avec le paquet util-linux.
Stéphane Chazelas
2
La plupart des plates-formes ont getopt, seul Linux AFAIK est livré avec une qui prend en charge les options longues ou les blancs dans les arguments. Les autres ne prendraient en charge que la syntaxe System V.
Stéphane Chazelas
7
getoptest une commande traditionnelle qui vient de System V bien avant la sortie de Linux. getoptn'a jamais été standardisé. Aucun de POSIX, Unix ou Linux (LSB) n'a standardisé la getoptcommande. getoptsest spécifié dans les trois mais sans prise en charge des options longues.
Stéphane Chazelas
1
Juste pour ajouter à cette discussion: ce n'est pas le traditionnel getopt. C'est la saveur linux-utils comme indiqué par @ StéphaneChazelas. Il a des options héritées qui désactiveront la syntaxe décrite ci-dessus, en particulier la page de manuel indique "GETOPT_COMPATIBLE force getopt à utiliser le premier format d'appel tel que spécifié dans le SYNOPSIS". Cependant, si vous pouvez vous attendre à ce que les systèmes cibles aient ce package installé, c'est tout à fait le chemin à parcourir, car le getopt d'origine est horrible et le getopt de Bash est très limité
n.caillou
1
Lien OP qui prétend que "les versions traditionnelles de getopt ne peuvent pas gérer les chaînes d'arguments vides ou les arguments avec des espaces blancs intégrés." et que la version util-linux de getopt ne doit pas être utilisée n'a aucune preuve et n'est plus exacte. Une enquête rapide (plus de 5 ans plus tard) montre que la version par défaut de getopt disponible à partir d'ArchLinux, SUSE, Ubuntu, RedHat, Fedora et CentOS (ainsi que la plupart des variantes dérivées) supporte toutes les arguments optionnels et les arguments avec des espaces.
mtalexan
10

Il y a ce getopts_long écrit comme une fonction shell POSIX que vous pouvez intégrer dans votre script.

Notez que Linux getopt(de util-linux) fonctionne correctement lorsqu'il n'est pas en mode traditionnel et prend en charge les options longues, mais n'est probablement pas une option pour vous si vous devez être portable sur d'autres Unices.

Les versions récentes de ksh93 ( getopts) et zsh ( zparseopts) ont un support intégré pour analyser les options longues qui pourraient être une option pour vous car elles sont disponibles pour la plupart des Unices (bien que souvent non installées par défaut).

Une autre option serait d'utiliser perlet son Getopt::Longmodule qui devrait être disponible sur la plupart des Unices de nos jours, soit en écrivant le script entier perlsoit en appelant simplement perl juste pour analyser l'option et envoyer les informations extraites au shell. Quelque chose comme:

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

Voyez perldoc Getopt::Longce qu'il peut faire et comment il diffère des autres analyseurs d'options.

Stéphane Chazelas
la source
2

Chaque discussion sur ce sujet met en évidence la possibilité d'écrire le code d'analyse manuellement - alors seulement vous pouvez être sûr de la fonctionnalité et de la portabilité. Je vous conseille de ne pas écrire de code que vous pouvez avoir généré et recréé par des générateurs de code open source faciles à utiliser. Utilisez Argbash , qui a été conçu pour fournir la réponse définitive à votre problème. Il s'agit d'un générateur de code bien documenté disponible sous forme d' application en ligne de commande , en ligne ou sous forme d' image Docker .

Je déconseille les bibliothèques bash, certaines d'entre elles utilisent getopt(ce qui rend tout à fait impossible à transporter) et il est difficile de regrouper un gigantesque blob shell illisible avec votre script.

bubla
la source
0

Vous pouvez utiliser getoptsur des systèmes qui le prennent en charge et utiliser une solution de repli pour les systèmes qui ne le font pas.

Par exemple, pure-getoptest implémenté dans Bash pur pour être un remplacement direct de GNU getopt.

Potherca
la source