Saltar al contenido

Usar getopts para procesar opciones de línea de comando largas y cortas

Esta división ha sido probado por especialistas así aseguramos la exactitud de nuestra esta reseña.

Solución:

getopt y getopts son bestias diferentes, y la gente parece no entender un poco lo que hacen. getopts es un comando incorporado para bash para procesar las opciones de la línea de comandos en un bucle y asignar cada opción y valor encontrados a su vez a las variables integradas, para que pueda seguir procesándolas. getopt, sin embargo, es un programa de utilidad externo, y en realidad no procesa sus opciones por usted la forma en que, por ejemplo, bash getopts, el Perl Getopt módulo o el Python optparse/argparse los módulos hacen. Todo lo que getopt Lo que hace es canonicalizar las opciones que se pasan, es decir, convertirlas a una forma más estándar, para que sea más fácil para un script de shell procesarlas. Por ejemplo, una aplicación de getopt podría convertir lo siguiente:

myscript -ab infile.txt -ooutfile.txt

dentro de esto:

myscript -a -b -o outfile.txt infile.txt

Tienes que hacer el procesamiento real tú mismo. No tienes que usar getopt en absoluto si establece varias restricciones en la forma en que puede especificar opciones:

  • solo ponga una opción por argumento;
  • todas las opciones van antes de cualquier parámetro posicional (es decir, argumentos que no son de opción);
  • para opciones con valores (p. ej. -o arriba), el valor debe ir como un argumento separado (después de un espacio).

Por que usar getopt en lugar de getopts? La razón básica es que solo GNU getopt le brinda soporte para opciones de línea de comandos con nombres largos.1 (ÑU getopt es el predeterminado en Linux. Mac OS X y FreeBSD vienen con un básico y no muy útil getopt, pero se puede instalar la versión GNU; vea abajo.)

Por ejemplo, aquí hay un ejemplo de uso de GNU getopt, de un guión mío llamado javawrap:

# NOTE: This requires GNU getopt.  On Mac OS X and FreeBSD, you have to install this
# separately; see below.
TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: 
             -n 'javawrap' -- "[email protected]"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

VERBOSE=false
DEBUG=false
MEMORY=
DEBUGFILE=
JAVA_MISC_OPT=
while true; do
  case "$1" in
    -v | --verbose ) VERBOSE=true; shift ;;
    -d | --debug ) DEBUG=true; shift ;;
    -m | --memory ) MEMORY="$2"; shift 2 ;;
    --debugfile ) DEBUGFILE="$2"; shift 2 ;;
    --minheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
    --maxheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

Esto le permite especificar opciones como --verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt" o similar. El efecto de la llamada a getopt es canonicalizar las opciones para --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt" para que pueda procesarlos más fácilmente. El citar alrededor "$1" y "$2" es importante ya que asegura que los argumentos con espacios en ellos se manejen correctamente.

Si borra las primeras 9 líneas (todo hasta el eval set línea), el código seguirá funcionando! Sin embargo, su código será mucho más exigente en cuanto al tipo de opciones que acepta: en particular, tendrá que especificar todas las opciones en la forma “canónica” descrita anteriormente. Con el uso de getopt, sin embargo, puede agrupar opciones de una sola letra, utilizar formas más cortas no ambiguas de opciones largas, utilizar la --file foo.txt o --file=foo.txt estilo, use el -m 4096 o -m4096 estilo, mezclar opciones y no opciones en cualquier orden, etc. getopt también genera un mensaje de error si se encuentran opciones no reconocidas o ambiguas.

NOTA: En realidad hay dos totalmente diferente versiones de getopt, básico getopt y GNU getopt, con diferentes funciones y diferentes convenciones de llamadas.2 Básico getopt está bastante roto: no solo no maneja opciones largas, sino que ni siquiera puede manejar espacios incrustados dentro de argumentos o argumentos vacíos, mientras que getopts hace esto bien. El código anterior no funcionará en básico getopt. ÑU getopt se instala de forma predeterminada en Linux, pero en Mac OS X y FreeBSD debe instalarse por separado. En Mac OS X, instale MacPorts (http://www.macports.org) y luego haga sudo port install getopt para instalar GNU getopt (generalmente en /opt/local/bin), y asegúrese de que /opt/local/bin está en tu camino de la cáscara por delante de /usr/bin. En FreeBSD, instale misc/getopt.

Una guía rápida para modificar el código de ejemplo para su propio programa: De las primeras líneas, todo es “repetitivo” que debería permanecer igual, excepto la línea que llama getopt. Debería cambiar el nombre del programa después -n, especifique opciones cortas después -oy opciones largas después --long. Coloque dos puntos después de las opciones que tengan un valor.

Finalmente, si ve un código que acaba de set en lugar de eval set, fue escrito para BSD getopt. Debería cambiarlo para usar el eval set estilo, que funciona bien con ambas versiones de getopt, mientras que la llanura set no funciona bien con GNU getopt.

1Realmente, getopts en ksh93 admite opciones de nombre largo, pero este shell no se usa con tanta frecuencia como bash. En zsh, usar zparseopts para obtener esta funcionalidad.

2Técnicamente, “GNU getopt“es un nombre inapropiado; esta versión fue escrita para Linux en lugar del proyecto GNU. Sin embargo, sigue todas las convenciones GNU, y el término” GNU getopt“se utiliza comúnmente (por ejemplo, en FreeBSD).

Hay tres implementaciones que se pueden considerar:

  • Bash incorporado getopts. Esto no admite nombres de opciones largos con guiones dobles prefix. Solo admite opciones de un solo carácter.

  • Implementación BSD UNIX de independiente getopt comando (que es lo que usa MacOS). Esto tampoco admite opciones largas.

  • Implementación GNU de independiente getopt. ÑU getopt(3) (usado por la línea de comandos getopt(1) en Linux) admite el análisis de opciones largas.


Algunas otras respuestas muestran una solución para usar el bash incorporado getopts para imitar opciones largas. Esa solución en realidad crea una opción corta cuyo carácter es “-“. Entonces obtienes “-” como bandera. Luego, cualquier cosa que siga se convierte en OPTARG, y prueba OPTARG con un anidado case.

Esto es inteligente, pero viene con salvedades:

  • getopts no puede hacer cumplir la especificación opt. No puede devolver errores si el usuario proporciona una opción no válida. Tiene que hacer su propia comprobación de errores mientras analiza OPTARG.
  • OPTARG se utiliza para el nombre de la opción larga, lo que complica el uso cuando la propia opción larga tiene un argumento. Termina teniendo que codificarlo usted mismo como un caso adicional.

Entonces, si bien es posible escribir más código para solucionar la falta de soporte para opciones largas, esto es mucho más trabajo y frustra parcialmente el propósito de usar un analizador getopt para simplificar su código.

La función getopts incorporada de Bash se puede usar para analizar opciones largas colocando un carácter de guión seguido de dos puntos en la optspec:

#!/usr/bin/env bash 
optspec=":hv-:"
while getopts "$optspec" optchar; do
    case "$optchar" in
        -)
            case "$OPTARG" in
                loglevel)
                    val="$!OPTIND"; OPTIND=$(( $OPTIND + 1 ))
                    echo "Parsing option: '--$OPTARG', value: '$val'" >&2;
                    ;;
                loglevel=*)
                    val=$OPTARG#*=
                    opt=$OPTARG%=$val
                    echo "Parsing option: '--$opt', value: '$val'" >&2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "$optspec:0:1" != ":" ]; then
                        echo "Unknown option --$OPTARG" >&2
                    fi
                    ;;
            esac;;
        h)
            echo "usage: $0 [-v] [--loglevel[=]]" >&2
            exit 2
            ;;
        v)
            echo "Parsing option: '-$optchar'" >&2
            ;;
        *)
            if [ "$OPTERR" != 1 ] || [ "$optspec:0:1" = ":" ]; then
                echo "Non-option argument: '-$OPTARG'" >&2
            fi
            ;;
    esac
done

Después de copiar al nombre del archivo ejecutable =getopts_test.sh en el directorio de trabajo actual, se puede producir una salida como

$ ./getopts_test.sh
$ ./getopts_test.sh -f
Non-option argument: '-f'
$ ./getopts_test.sh -h
usage: code/getopts_test.sh [-v] [--loglevel[=]]
$ ./getopts_test.sh --help
$ ./getopts_test.sh -v
Parsing option: '-v'
$ ./getopts_test.sh --very-bad
$ ./getopts_test.sh --loglevel
Parsing option: '--loglevel', value: ''
$ ./getopts_test.sh --loglevel 11
Parsing option: '--loglevel', value: '11'
$ ./getopts_test.sh --loglevel=11
Parsing option: '--loglevel', value: '11'

Obviamente no consigue ni realiza OPTERR comprobar ni analizar el argumento de opción para las opciones largas. El fragmento de script anterior muestra cómo se puede hacer esto manualmente. El principio básico también funciona en el shell de Debian Almquist (“guión”). Tenga en cuenta el caso especial:

getopts -- "-:"  ## without the option terminator "-- " bash complains about "-:"
getopts "-:"     ## this works in the Debian Almquist shell ("dash")

Tenga en cuenta que, como señala GreyCat de http://mywiki.wooledge.org/BashFAQ, este truco explota un comportamiento no estándar del shell que permite el argumento de opción (es decir, el nombre de archivo en “-f nombre de archivo”) para ser concatenados a la opción (como en “-filename”). El estándar POSIX dice que debe haber un espacio entre ellos, que en el caso de “- longoption” terminaría el análisis de opciones y convertiría todas las opciones largas en argumentos que no sean opciones.

Comentarios y puntuaciones

Finalizando este artículo puedes encontrar las ilustraciones de otros administradores, tú asimismo tienes el poder dejar el tuyo si lo crees conveniente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *