2010-04-27 24 views
10

Acabo de descubrir que getopt no es multiplataforma (en particular para FreeBSD y Linux). ¿Cuál es la mejor solución para este problema?Getopt multiplataforma para un script de shell

+0

Si ha iniciado a cabo usando getopt GNU con largas opciones, etc., y están buscando para hacer su código existente portátil con el menor número de cambios posible, entonces puede encontrar útil http://stackoverflow.com/a/37087374/324105. – phils

Respuesta

14

Uso getopts (con una "s").

Según Bash FAQ 35:

A menos que sea la versión de util-linux, y utiliza su modo avanzado, no utilice nunca getopt (1). getopt no puede manejar cadenas de argumentos vacíos, o argumentos con espacios en blanco incrustados. Por favor, olvide que alguna vez existió.

El shell POSIX (y otros) ofrecen getopts que es seguro de usar en su lugar.

+0

Uso el mnemotécnico "S" para Shell, getopt "S" está integrado en BASH –

+0

@Dennis Williamson Actualice su respuesta: Según la entrada de preguntas frecuentes (actualizada): ** A menos que sea la versión de util-linux, y usted use su modo avanzado, nunca use getopt (1). ... ** – Florian

+0

@Florian: actualizado. Gracias. –

0

La sintaxis básica para getopt es multiplataforma.

getopt vi: -v -i 100 file 
27

Existen esencialmente dos versiones del comando getopt: la versión original y la versión mejorada de GNU. La versión mejorada de GNU es retrocompatible con la versión original, por lo que si solo utiliza las características de la versión original, funcionará con ambas.

Detectar la versión de getopt está disponible

Puede detectar qué versiones y utiliza las características mejoradas si la versión de GNU mejorada está disponible, y se limita a las características originales si la versión de GNU mejorado es no disponible. La versión mejorada tiene una opción -T para probar qué versión está disponible.

getopt -T > /dev/null 
if [ $? -eq 4 ]; then 
    # GNU enhanced getopt is available 
    set -- `getopt --long help,output:,version --options ho:v -- "[email protected]"` 
else 
    # Original getopt is available 
    set -- `getopt ho:v "[email protected]"` 
fi 

Considere el uso de comandos shell integrado getopts (con una "s") en lugar, porque es más portátil. Sin embargo, getopts no admite opciones largas (por ejemplo, --help).

Si te gusta las opciones largas, utilizar getopt y el uso de la prueba anterior para ver si la versión mejorada de GNU getopt está disponible o no. Si la versión mejorada no está disponible, la secuencia de comandos puede degradarse con gracia utilizando la versión original de getopt (sin soporte para nombres largos de opción y sin soporte de espacio en blanco) o usando getopts (sin soporte para nombres de opción largos).

Usando GNU getopt mejorado adecuadamente

Obtener la versión mejorada de GNU para procesar argumentos con espacios en blanco correctamente es difícil. Así es como se hace:

ARGS=`getopt --long help,output:,verbose --options ho:v -- "[email protected]"` 
if [ $? -ne 0 ]; then 
    echo "Usage error (use -h for help)" >&2 
    exit 2 
fi 
eval set -- $ARGS 

# Parameters are now sorted: options appear first, followed by --, then arguments 
# e.g. entering: "foo bar" -o abc baz -v 
#  produces: -o 'abc' -v -- 'foo bar' 'baz' 

El secreto es usar "[email protected]" donde las comillas dobles son muy importantes (en la línea 1), y para el evalestablece de comandos (en la línea 6).

Para detectar y manejar los errores provocados por getopt, la llamada a getopt se realiza por separado desde eval con los dos vinculados por la variable ARGS.

ejemplo de trabajo completa

PROG=`basename $0` 

getopt -T > /dev/null 
if [ $? -eq 4 ]; then 
    # GNU enhanced getopt is available 
    ARGS=`getopt --name "$PROG" --long help,output:,verbose --options ho:v -- "[email protected]"` 
else 
    # Original getopt is available (no long option names, no whitespace, no sorting) 
    ARGS=`getopt ho:v "[email protected]"` 
fi 
if [ $? -ne 0 ]; then 
    echo "$PROG: usage error (use -h for help)" >&2 
    exit 2 
fi 
eval set -- $ARGS 

while [ $# -gt 0 ]; do 
    case "$1" in 
     -h | --help)  HELP=yes;; 
     -o | --output) OUTFILE="$2"; shift;; 
     -v | --verbose) VERBOSE=yes;; 
     --)    shift; break;; # end of options 
    esac 
    shift 
done 

if [ $# -gt 0 ]; then 
    # Remaining parameters can be processed 
    for ARG in "[email protected]"; do 
    echo "$PROG: argument: $ARG" 
    done 
fi 

echo "$PROG: verbose: $VERBOSE" 
echo "$PROG: output: $OUTFILE" 
echo "$PROG: help: $HELP" 

Este ejemplo se puede descargar desde https://gist.github.com/hoylen/6607180

La tabla de comparación en Wikipedia's entry on getopts comparan las diferentes características.

+0

Sí, 'getopts' (con una s) es otra opción. Los puristas de la portabilidad preferirían aún el getopt, ya que los getopts no estaban disponibles en las antiguas conchas de Bourne antes de 1986, pero esa es una razón de peso porque la mayoría de las conchas modernas son compatibles. Una mejor razón es hacer que sea más fácil aprovechar el complemento mejorado de GNU si está disponible. El getopt mejorado de GNU permite mezclar operandos con opciones y admite nombres de opciones largos (ambas características que getopts no admite - vea [tabla de comparación] [1]). – Hoylen

+0

¿Por qué imprime un '--' vacío cuando ejecuto' eval set - $ ARGS'? Es muy irritante – Hindol