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

13

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

Víctor
fuente
Etiqueta para lo interno getopts, pero está utilizando el /usr/bin/getoptcomando.
Anthon
@Anthon Lo siento, he usado la etiqueta incorrecta, pero no tengo suficientes reputaciones para agregar otra etiqueta, que necesita 300 reputaciones. Sin embargo, he eliminado la etiqueta incorrecta en este momento.
Victor

Respuestas:

15

getoptestá perfectamente bien sin tener opciones cortas. Pero debe decirle que no tiene opciones cortas. Es una peculiaridad en la sintaxis, del manual:

Si hay -oo --optionsopción se encuentra en la primera parte, el primer parámetro de la segunda parte se usa como la cadena de opciones cortas.

Eso es lo que sucede en su prueba: getopt -l long-key -- --long-key footrata --long-keycomo la lista de opciones -egklnoyy foocomo el único argumento. Utilizar

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'
Gilles 'SO- deja de ser malvado'
fuente
¿El OP se mezcló getoptse getoptinfectó con su respuesta? Empiezas comentando y getoptsluego solo mencionas getopt.
Anthon
@Anthon Toda mi respuesta es sobre el getoptprograma de GNU coreutils, que es de lo que se trata la pregunta. He arreglado el texto que decía getopts. Gracias. getoptsni siquiera tiene opciones largas, por lo que nada de esto se aplicaría getopts.
Gilles 'SO- deja de ser malvado'
El OP originalmente tenía la getoptsetiqueta. No quería cambiar tu respuesta, porque normalmente sabes mucho mejor que yo sobre lo que estás escribiendo :-)
Anthon
Perdí casi una hora tratando de resolver esto. Me acabas de salvar unos vanos sorbos de café. Gracias ... aprovechemos mejor este café ahora. ☕️
dmmd
1

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

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

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

Sospecho que lo mismo es cierto, getoptpero no sé lo suficiente para decirlo con certeza. Dada la siguiente matriz arg, demostraré mi propio analizador arg pequeño, que depende principalmente de la relación de evaluación / asignación por la 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 arg 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 arg de una de dos formas 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 a la matriz arg.

Si lo llamas así:

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

Su primer orden del día 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 la función se evalúa cuando el shell de llamada construye los documentos de entrada de la función aquí, pero acase()nunca se llama o define en el shell de llamada. Sin embargo, se llama en la subshell, por lo que 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 se completa acase()con coincidencias para todos los argumentos que comienzan con la cadena --.

La función realiza prácticamente todo su procesamiento en la subshell, guardando iterativamente cada uno de los valores de los argumentos en los alias asignados con nombres asociativos. Cuando aliastermina , imprime todos los valores con los que guardó , que está especificado por POSIX para imprimir todos los valores guardados citados de tal manera que sus valores puedan reintroducirse 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 el bloque de casos coincide. Si encuentra una coincidencia allí, arroja una bandera - f=optname. Hasta que una vez más encuentre una opción válida, agregará cada argumento posterior a una matriz que construya en función del indicador actual. Si la misma opción se especifica varias veces, los resultados se combinan y no se anulan. Cualquier cosa que no esté en el caso, o cualquier argumento que siga a las opciones ignoradas, se asignan a una matriz ignorada .

La salida es segura para la entrada de shell automáticamente por el shell, y así:

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

... debería ser perfectamente seguro. Si por alguna razón no es seguro, entonces probablemente debería presentar un informe de error con su encargado de mantenimiento de shell.

Asigna dos tipos de valores de alias para cada coincidencia. Primero, establece un indicador: esto ocurre independientemente de si una opción precede a argumentos no coincidentes. Por lo tanto, cualquier aparición --flagen la lista arg se disparará flag=1. Esto no se complica, --flag --flag --flagsolo se pone flag=1. Sin embargo, este valor se incrementa para cualquier argumento que pueda seguirlo. Se puede usar 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

SALIDA

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 para los argumentos que no coincidían, sustituiría ignorado en 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
mikeserv
fuente