¿Cómo puedo detectar que no se pasaron opciones con getopts?

19

Tengo este código

#getoptDemo.sh
usage()
{
    echo "usage: <command> options:<w|l|h>"
}
while getopts wlh: option
do
    case $option in
            (w)
                    name='1';;
            (l)
                    name='2';;
            (h)
                    name='3';;
            (*)
                    usage
                    exit;;
    esac
done
print 'hi'$name

Cuando ejecuto bash getoptDemos.sh(sin la opción) se imprime en hilugar de llamar a la función usage. Llama al uso cuando se dan opciones distintas de w, h y l. Entonces no puede funcionar cuando no se especifican opciones.

He intentado usar ?, \?, :en lugar de *, pero no puedo lograr lo que quería. Me refiero a todo el docsen getoptlo dice de usar ?.

¿Qué estoy haciendo mal?

Hussain Tamboli
fuente
¿Con qué shell lo estás ejecutando?
Julian
Lo estoy ejecutando bajo/bin/bash
Hussain Tamboli

Respuestas:

15

Cuando ejecuta este script sin ninguna opción, getopt devolverá falso, por lo que no entrará en el bucle en absoluto. Simplemente se desplegará a la impresión: ¿es esto ksh / zsh?

Si debe tener una opción, lo mejor es probar $ name después del ciclo.

if [ -z "$name" ]
then
   usage
   exit
fi

Pero asegúrese de que esté $namevacío antes de llamar getopts(ya que podría haber un $nameen el entorno que el shell recibió al inicio) con

unset name

(antes del getoptsbucle)

Julian
fuente
No. es bash. Entonces, ¿cómo puedo lograr lo que quiero? Manejar la no argumentcondición usando bash.
Hussain Tamboli
Si desea una opción obligatoria, no creo que sea posible, probablemente sea por eso que se llaman opciones :) Sin embargo, puede probar $ name después del ciclo para asegurarse de que se haya configurado. si [-z "$ nombre"]; luego uso; salida ; fi
Julián
Gracias. El código anterior realmente ayudó. Es una pena getoptsque no tenga esa disposición. Lo que es peor que eso es que no puede votarte.
Hussain Tamboli
¿Qué pasa si estaba ejecutando otro shell? zsh, sh. ¿Alguna cáscara que no sea bash se ocupa de esta condición?
Hussain Tamboli
20

getoptsprocesa las opciones a su vez. Ese es su trabajo. Si el usuario no pasa ninguna opción, la primera invocación de getoptssale del ciclo while.

Si ninguna de las opciones toma un argumento, el valor de OPTINDindica cuántas opciones se pasaron. En general, OPTINDes el número de argumentos que son opciones o argumentos a opciones, en oposición a los argumentos que no son opciones (operandos).

while getopts …; do …; done
if [ $OPTIND -eq 1 ]; then echo "No options were passed"; fi
shift $((OPTIND-1))
echo "$# non-option arguments"

En cualquier caso, no está tratando de determinar si no había opciones, sino si no namese pasó ninguna de las opciones de configuración. Por lo tanto, compruebe si nameestá desarmado (y tenga cuidado de desarmarlo primero) .

Gilles 'SO- deja de ser malvado'
fuente
1
Gracias. +1 para ti cuando pongo las últimas 3 líneas de su código en sample.sh y lo ejecuto bash sample.sh -abc file.txtda - 1 non-option arguments. ¿Cómo puedo saber cuántas opciones se dieron? (aquí 3)
Hussain Tamboli
1
Extraño, nadie ha comentado sobre este pequeño error: si no se dan opciones, OPTIND será 1 después de ese ciclo 'while getopts ...'. Por lo tanto, la verificación if debería verificar la igualdad con 1, no con cero.
Daniel
4

Si su script debe recibir argumentos de opción, en cualquier caso, coloque este bloque al principio (antes de los getops).

if [[ ! $@ =~ ^\-.+ ]]
then
  #display_help;
fi

Block verifica que la cadena de parámetro no comience con el -símbolo, lo que indica que el primer parámetro no es un argumento de opción.

mcounad
fuente
2

Lo comprobaría con una variable. Si getopts nunca pasa el ciclo en caso de que no haya argumento, puede usarlo, por ejemplo, de esta manera:

#getoptDemo.sh
usage()
{
    echo "usage: <command> options:<w|l|h>"
}

no_args="true"
while getopts wlh: option
do
    case $option in
            (w)
                    name='1';;
            (l)
                    name='2';;
            (h)
                    name='3';;
            (*)
                    usage
                    exit;;
    esac
    no_args="false"
done

[[ "$no_args" == "true" ]] && { usage; exit 1; }

print 'hi'$name
Imre Kneifel
fuente
0

Justo antes de su getoptsbloqueo, verifique si $1(el primer argumento / opción que pasó en la línea de comando) es igual a una cadena vacía. Si es así, imprima el mensaje de uso y salga (o ejecute alguna función "sin opciones" si es un anarquista), de lo contrario, getoptsanalice las opciones como de costumbre.

La razón por la que esta característica no está incluida en getopts, es porque ya puedes lograrlo en bash con un "if-else". Ejemplo:

if [[ $1 == "" ]]; then
    Your_Usage_Function;
    exit;
else
   #parse options with getopts code block here;
fi

¿Tener sentido?

codrcodz
fuente