¿Cómo usar getopt en la línea de comando bash con solo opciones largas?

¿Cómo usar getopt en la línea de comando bash con solo opciones largas?

Hay un getoptcomando en la línea de comando de bash. getoptSe puede usar con opciones cortas (como getopt -o axby "$@"), y se puede usar con opciones cortas y largas (como getopt -o axby -l long-key -- "$@"), pero ahora necesitosoloopciones largas (es decir, las opciones cortas no existen en absoluto), sin embargo, el comando getopt -l long-key -- "$@"no analiza --long-keyla opción correctamente. Entonces, ¿cómo puedo usar getoptel comando consoloopciones largas? ¿O es imposible o es solo un error del getoptcomando?

Respuesta1

getoptEstá perfectamente bien no tener opciones cortas. Pero debes decirle que no tienes opciones cortas. Es una peculiaridad en la sintaxis, del manual:

Si no se encuentra -oninguna --optionsopción en la primera parte, el primer parámetro de la segunda parte se utiliza como cadena de opciones corta.

Eso es lo que está sucediendo en su prueba: getopt -l long-key -- --long-key foose trata --long-keycomo la lista de opciones -egklnoyy foocomo el único argumento. Usar

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

p.ej

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

Respuesta2

No sé, getoptpero el getoptsincorporado se puede usar para manejar solo opciones largas como esta:

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

Por supuesto, tal como están las cosas, eso no funciona si se supone que las opciones largas tienen argumentos. Sin embargo, se puede hacer, pero, como he aprendido, estoy trabajando en esto. Si bien inicialmente lo incluí aquí, me di cuenta de que para opciones largas no tiene mucha utilidad. En este caso, solo se trataba de acortar mis case (match)campos en un carácter único y predecible. Ahora, lo que sé es que es excelente para opciones cortas: es más útil cuando recorre una cadena de longitud desconocida y selecciona bytes individuales de acuerdo con su cadena de opciones. Pero cuando la opciónesel argumento, no hay mucho que estés haciendo con una for var do case $var incombinación que pueda hacer. Creo que es mejor mantenerlo simple.

Sospecho que ocurre lo mismo, getoptpero no sé lo suficiente al respecto para decirlo con certeza. Dada la siguiente matriz de argumentos, demostraré mi propio pequeño analizador de argumentos, que depende principalmente de la relación de evaluación/asignación que he llegado a apreciar aliasy $((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

Esa es la cadena de argumento con la que trabajaré. Ahora:

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

Eso procesa la matriz de argumentos de una de dos maneras diferentes dependiendo de si le entrega uno o dos conjuntos de argumentos separados por el --delimitador. En ambos casos se aplica a secuencias de procesamiento de la matriz arg.

Si lo llamas así:

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

Su primera tarea será escribir su acase()función para que se vea así:

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

Y al lado de shift 3. La sustitución de comandos en la acase()definición de función se evalúa cuando el shell que llama crea los documentos aquí de entrada de la función, pero acase()nunca se llama ni se define en el shell que llama. Sin embargo, se llama en el subshell, por supuesto, y de esta manera puede especificar dinámicamente las opciones de interés en la línea de comando.

Si le entrega una matriz no delimitada, simplemente la llena acase()con coincidencias para todos los argumentos que comienzan con la cadena --.

La función realiza prácticamente todo su procesamiento en el subshell: guarda iterativamente cada uno de los valores del argumento en alias asignados con nombres asociativos. Cuando termina, imprime todos los valores guardados alias, que están especificados en POSIX para imprimir todos los valores guardados citados de tal manera que sus valores se puedan volver a ingresar en el shell. Entonces cuando lo hago...

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

Su salida se ve así:

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

A medida que recorre la lista de argumentos, comprueba si hay una coincidencia en el bloque de casos. Si encuentra una coincidencia allí, arroja una bandera - f=optname. Hasta que vuelva a encontrar una opción válida, agregará cada argumento posterior a una matriz que construye en función del indicador actual. Si se especifica la misma opción varias veces, los resultados se componen y no se anulan. Todo lo que no esté entre mayúsculas y minúsculas, o cualquier argumento que siga a opciones ignoradas, se asigna a unignoradoformación.

La salida está protegida automáticamente por el shell para la entrada del shell, por lo que:

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

...debería ser perfectamente seguro. Si es por alguna razónno esseguro, entonces probablemente debería presentar un informe de error al encargado del shell.

Asigna dos tipos de valores de alias para cada coincidencia. En primer lugar, establece una bandera; esto ocurre independientemente de que una opción preceda o no a argumentos que no coinciden. Por lo tanto, cualquier aparición de --flagen la lista de argumentos activará flag=1. Esto no agrava, --flag --flag --flagsimplemente empeora flag=1. Este valorhaceSin embargo, incremente, para cualquier argumento que pueda seguirlo. Se puede utilizar como clave de índice. Después de hacer lo evalanterior puedo hacer:

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

...Llegar...

8
1

Y entonces:

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

PRODUCCIÓN

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

Y a los argumentos que no coincidían los sustituiríaignoradoen el for ... incampo anterior para obtener:

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

información relacionada