¿Cómo manejo los interruptores en un script de shell?

17

¿Hay algunas herramientas integradas que reconocerán -xy --xxxxcomo modificadores, y no argumentos, o tiene que revisar todas las variables de entrada, probar guiones y luego analizar los argumentos a partir de entonces?

usuario394
fuente

Respuestas:

16

Uso getopts.

Es bastante portátil ya que está en la especificación POSIX. Lamentablemente no admite opciones largas.

Consulte también este getopts tutorial, cortesía de la wiki de bash-hackers y esta pregunta de stackoverflow.

Si solo necesita opciones cortas, el patrón de uso típico para getopts(utilizando informes de error no silenciosos) es:

# process arguments "$1", "$2", ... (i.e. "$@")
while getopts "ab:" opt; do
    case $opt in
    a) aflag=true ;; # Handle -a
    b) barg=$OPTARG ;; # Handle -b argument
    \?) ;; # Handle error: unknown option or missing required argument.
    esac
done
jw013
fuente
3
Debe mencionarse que getoptsiempre debe verificarse como GNU getopt antes de usarlo, pero no debe usarlo de todos modos ya que getoptses más portátil (y generalmente más agradable) de todos modos. Si haces necesidad de llamar por alguna razón, lo llaman de una manera específica de GNU, y asegurarse de que GETOPT_COMPATIBLEno se encuentra en el medio ambiente.
Chris Down
¿Qué hace el colon en while getopts "ab:" opthacer?
usuario394
1
@ user394 :Después de una letra de opción significa que requiere un argumento. A :como primer carácter significa suprimir los mensajes de error.
jw013
22

Supongo que estás usando bash o similar. Un ejemplo:

all=false
long=false

while getopts ":hal" option; do
  case $option in
    h) echo "usage: $0 [-h] [-a] [-l] file ..."; exit ;;
    a) all=true ;;
    l) long=true ;;
    ?) echo "error: option -$OPTARG is not implemented"; exit ;;
  esac
done

# remove the options from the positional parameters
shift $(( OPTIND - 1 ))

ls_opts=()
$all && ls_opts+=( -a )
$long && ls_opts+=( -l )

# now, do it
ls "${ls_opts[@]}" "$@"
Glenn Jackman
fuente
1
+1 para usar +=con una matriz. No sabía que podías hacer eso. ¡Agradable!
James Sneeringer
5

Tienes que escribir un ciclo para analizar los parámetros. De hecho, puede usar el getoptscomando para hacerlo fácilmente.

Este es un ejemplo simple de getoptsla página del manual:

aflag=
bflag=
while getopts ab: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval="$OPTARG";;
    ?)    printf "Usage: %s: [-a] [-b value] args\n" $0
          exit 2;;
    esac
done
if [ ! -z "$aflag" ]; then
    printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
    printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"
andcoz
fuente
2

Recientemente escribí un script para el trabajo que era versátil y permitía múltiples tipos de interruptores en cualquier orden. No puedo revelar el guión completo por razones legales obvias (sin mencionar que no lo tengo conmigo en este momento), pero aquí está el meollo del asunto ... puede ponerlo en una subrutina y llamarlo al final de tu guión:

options () {

    if [ -n "$1" ]; then # test if any arguments passed - $1 will always exist
        while (( "$#" )); do  # process ALL arguments
            if [ "$1" = ^-t$ ]; then # -t short for "test"
                # do something here THEN shift the argument
                # shift removes it from $@ and reduces $# by
                # one if you supply no argument
                shift

            # we can also process multiple arguments at once
            elif [[ "$1" =~ ^--test=[:alnum:]{1,8}$ ]] && [[ "$2" =~ ^-t2$ ]] && [[ -n "$3" ]]; then # check for 3 arguments
                # do something more then remove them ALL from the arg list    
                shift 3
            else
                echo "No matching arguments!"
                echo "Usage: [script options list here]"
            fi
        done
    else
        echo "Usage: [script options list here]"
        exit 0
    fi
}

options "$@" # run options and loop through/process ALL arguments

Recomiendo limitar su script bash a menos de 400 líneas / 15k caracteres; mi guión antes mencionado creció más allá de este tamaño y se volvió muy difícil trabajar en él. Comencé a reescribirlo en Perl, que es mucho más adecuado para la tarea. Tenga eso en cuenta mientras trabaja en sus scripts en bash. Bash es ideal para pequeños scripts y líneas, pero cualquier cosa más compleja y es mejor escribirlo en Perl.

Tenga en cuenta que no he probado lo anterior, por lo que probablemente no funcione, pero se entiende la idea general.

sombreador
fuente
La forma de llamar optionsal final no es correcta, volverá -bash: syntax error near unexpected token $@. Llámalo como options "$@".
Chris Down
Sí, mezclé Perl y Bash. Corregido
laebshade
Esa whilecondición no debería ser (($#))en su lugar?
manatwork
¿Por qué necesitarías dos paréntesis $#? Editar: tienes razón. Lo arregló awhile (( "$#" ))
laebshade