Comment utiliser getopt en ligne de commande bash avec seulement des options longues?

13

Il y a une getoptcommande en ligne de commande bash. getoptpeut être utilisé avec des options courtes (telles que getopt -o axby "$@") et peut être utilisé avec des options courtes et longues (telles que getopt -o axby -l long-key -- "$@"), mais maintenant je n'ai besoin que d' options longues (c'est-à-dire que les options courtes n'existent pas du tout), mais la commande getopt -l long-key -- "$@"ne fonctionne pas analyser --long-keycorrectement l'option. Alors, comment puis-je utiliser la getoptcommande avec seulement des options longues? Ou est-ce impossible ou est-ce simplement un bug de la getoptcommande?

Victor
la source
Vous balisez pour l'interne getopts, mais vous utilisez la /usr/bin/getoptcommande.
Anthon
@Anthon Désolé, j'ai utilisé la mauvaise balise, mais je n'ai pas assez de réputation pour ajouter une autre balise, qui a besoin de 300 réputations. Cependant, je viens de supprimer la mauvaise balise.
Victor

Réponses:

15

getoptest parfaitement bien sans avoir d'options courtes. Mais vous devez lui dire que vous n'avez pas d'options courtes. C'est une bizarrerie dans la syntaxe - du manuel:

Si aucune -oou --optionsoption n'est trouvée dans la première partie, le premier paramètre de la deuxième partie est utilisé comme chaîne d'options courte.

C'est ce qui se passe dans votre test: getopt -l long-key -- --long-key footraite --long-keycomme la liste des options -egklnoyet foocomme seul argument. Utilisation

getopt -o '' -l long-key -- "$@"

par exemple

$ 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'
Gilles 'SO- arrête d'être méchant'
la source
Le mélange du PO a-t- getoptsil getoptinfecté votre réponse? Vous commencez par commenter getoptspuis ne mentionnez que getopt.
Anthon
@Anthon Toute ma réponse concerne le getoptprogramme de GNU coreutils, c'est de cela qu'il s'agit. J'ai corrigé le texte qui disait getopts. Merci. getoptsne fait même pas d'options longues, donc rien de tout cela ne s'appliquerait getopts.
Gilles 'SO- arrête d'être méchant'
L'OP avait à l'origine l' getoptsétiquette. Je ne voulais pas changer votre réponse, parce que vous savez normalement beaucoup mieux que moi de quoi vous écrivez :-)
Anthon
J'ai perdu presque une heure à essayer de comprendre cela. Vous venez de me sauver quelques vaines gorgées de café. Merci ... permet maintenant de mieux utiliser ce café. ☕️
dmmd
1

Je ne sais pas getoptmais la fonction getoptsintégrée ne peut être utilisée que pour gérer de longues options comme ceci:

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

Bien sûr, en l'état, cela ne fonctionne pas si les options longues sont censées avoir des arguments. Cela peut être fait, cependant, mais, comme je l'ai appris en travaillant là-dessus. Bien que je l'ai initialement inclus ici, j'ai réalisé que pour les options longues, il n'avait pas beaucoup d'utilité. Dans ce cas, cela ne faisait que raccourcir mes case (match)champs d'un seul caractère prévisible. Maintenant, ce que je sais, c'est qu'il est excellent pour les options courtes - il est plus utile lorsqu'il fait une boucle sur une chaîne de longueur inconnue et sélectionne des octets uniques en fonction de sa chaîne d'options. Mais lorsque l'option est l'argument, vous ne faites pas grand-chose avec une for var do case $var incombinaison qu'elle pourrait faire. Il vaut mieux, je pense, rester simple.

Je soupçonne qu'il en va de même, getoptmais je n'en sais pas assez pour le dire avec certitude. Étant donné le tableau d'arguments suivant, je vais démontrer mon propre petit analyseur d'arguments - qui dépend principalement de la relation d'évaluation / affectation que j'ai appris à apprécier pour aliaset $((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

C'est la chaîne d'argument avec laquelle je vais travailler. Maintenant:

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

Cela traite le tableau arg de deux manières différentes selon que vous lui remettez un ou deux ensembles d'arguments séparés par le --délimiteur. Dans les deux cas, il s'applique aux séquences de traitement du tableau arg.

Si vous l'appelez comme:

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

Son premier ordre du jour sera d'écrire sa acase()fonction pour ressembler à:

acase() case "$a" in 
    (--lopt1) f=lopt1; aset "?$(($f)):";;
    (--lopt2) f=lopt2; aset "?$(($f)):";;
    (--*) f=ignored; aset "?$(($f)):";;
    (*) aset "" "$a";;esac

Et à côté de shift 3. La substitution de commande dans la acase()définition de la fonction est évaluée lorsque le shell appelant crée les documents d'entrée de la fonction ici, mais acase()n'est jamais appelée ou définie dans le shell appelant. Il est appelé dans le sous-shell, bien sûr, et donc de cette façon, vous pouvez spécifier dynamiquement les options d'intérêt sur la ligne de commande.

Si vous lui donnez un tableau non délimité, il remplit simplement acase()avec des correspondances pour tous les arguments commençant par la chaîne --.

La fonction effectue pratiquement tout son traitement dans le sous-shell - sauvegarde itérativement chacune des valeurs de l'argument dans des alias attribués avec des noms associatifs. Une aliasfois terminé, il imprime toutes les valeurs enregistrées avec - qui est spécifié par POSIX pour imprimer toutes les valeurs enregistrées citées de telle manière que leurs valeurs puissent être réintroduites dans le shell. Alors quand je fais ...

aopts --lopt1 --lopt2 -- "$@"

Sa sortie ressemble à ceci:

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

En parcourant la liste d'arguments, il vérifie la correspondance avec le bloc de cas. S'il y trouve une correspondance, il lance un drapeau - f=optname. Jusqu'à ce qu'il trouve à nouveau une option valide, il ajoutera chaque argument suivant à un tableau qu'il construit en fonction de l'indicateur actuel. Si la même option est spécifiée plusieurs fois, les résultats sont composés et ne remplacent pas. Tout ce qui n'est pas au cas où - ou tout argument suivant des options ignorées - est affecté à un tableau ignoré .

La sortie est protégée par le shell pour une entrée de shell automatiquement par le shell, et donc:

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

... devrait être parfaitement sûr. Si, pour une raison quelconque, il n'est pas sûr, vous devez probablement déposer un rapport de bogue auprès de votre responsable du shell.

Il attribue deux types de valeurs d'alias pour chaque correspondance. Tout d'abord, il définit un indicateur - cela se produit, qu'une option précède ou non les arguments non correspondants. Ainsi, toute occurrence de --flagdans la liste arg se déclenchera flag=1. Cela n'aggrave pas - --flag --flag --flagobtient juste flag=1. Cette valeur ne se incrément - pour tous les arguments qui pourraient le suivre. Il peut être utilisé comme clé d'index. Après avoir fait ce qui evalprécède, je peux faire:

printf %s\\n "$lopt1" "$lopt2"

...obtenir...

8
1

Et donc:

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

PRODUCTION

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

Et aux arguments qui ne correspondent pas, je remplacerais ignoré dans le for ... inchamp ci-dessus pour obtenir:

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
mikeserv
la source