miércoles, noviembre 07, 2012

Jugando con getopt y getopts

Cuando me he tenido que enfrentar al problema de controlar el paso parámetros en los scripts, siempre lo he dejado como una tarea de google, es decir, que busco por encima lo que necesito, lo adapto a mis necesidades y luego lo olvido. Para la próxima vez, disponde de esta nota, que pretendía ser breve, pero que al final no lo será tanto.

¿Diferencias entre getopt y getopts?

Getopt, en singular, es un comando que permite tratar los parámetros que llegan, sean cortos (del tipo -a, -b, -l) o largos (--long). El segundo es un comando incluido dentro de la bash que solo nos permite operar con los parámetros cortos.

¿Parámetros cortos y largos, con o sin argumentos?

Tanto los parámetros cortos como los largos, pueden tener argumentos (obligatorios o no). Esto se especificará en la cadena de parámetros que se le pasará a getopt/getopts. Por ejemplo, para indicar que vamos a tener dos parámetros sin argumentos podemos utilizar "ab". Si el parámetro 'a' va a llevar un argumento, pondremos "a:b". Si ambos llevan argumento "a:b:". Por último hay un caso más que podemos necesitar, que el parámetro 'a' pueda llevar un argumento, pero que no sea obligatoria la aparición del argumento, entonces la cadena de tratamiento sería 'a::b".

En el caso de los parámetros largos es algo similar. Tenemos el parámetro '--help' y el parámetro '--output=file' con un argumento. La cadena de tratamiento queda como 'help,output:'. Si queremos que '--help' lleve un argumento extra opcional, entonces tendríamos 'help::,output:'. Observese que en este caso, para separar los parámetros largos se utiliza la coma.

¿Cómo usar getopt?

La utilización de getopt es algo compleja de entender, pero con la siguiente estructura de código tenemos el análisis de todos los parametros que le pueden llegar a nuestros scripts con solo unas pequeñas modificaciones (ver ejemplo un poco más abajo).

La estructura va como sigue. Primero se costruye una cadena que contiene la ejecución del tratamiento de parámetro con getopt, indicando los parámetros cortos (-o) y sus versiones largas (--long). Se le pasa también el nombre del programa (-n), se indica cuando hay que dejar de procesar los parámetros (si se encuentra '--') y por último se le pasa la ristra de parámetros que se han recibido ($@). Si hay algun error en el tratamiento, saldremos.

El siguiente paso es evaluar la cadena, es en este momento donde entra en juego el uso de las comillas invertidas en su definición, utilizando set --, para hacer que los argumentos sean evaluados en función de su posición. Con la operación shift iremos desplazándonos entre los argumentos cuando entremos en el bucle, del que saldremos cuando topemos con el parámetro --, que indica que hemos llegado al final de los argumentos recibidos.

Dentro del bucle, hacemos el tratamiento con los case necesarios para los argumentos y si estamos en un argumento que tiene alguna opción, la recogemos al estar en la variable $2 ($1 es la propia opción). Siempre que tratemos un argumento habra que hacer el desplazamiento con shift (uno o dos veces dependiendo de si tiene opción o no), de lo contrario nos quedaremos en el bucle para siempre...
CADPARAM=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
     -n '$0' -- "$@"`
if [ $? != 0 ] ; then
      echo "Error en el tratamiento de parametros..." 1>&2 ;
      exit 1 ;
fi
# Se hace la evaluacion de los parametros
eval set -- "$CADPARAM"
while true ; do
        case "$1" in
                -a|--a-long) echo "Opcion a" ; shift ;;
                -b|--b-long) echo "Opcion b, argumento \`$2'" ; shift 2 ;;
                -c|--c-long)
                        # c tiene un argumento opcional.
                        # Sera generado vacio aunque no lo encuentre.
                        case "$2" in
                                "") echo "Opcion c, sin argumento"; shift 2 ;;
                                *)  echo "Opcion c, con argumento \`$2'" ; shift 2 ;;
                        esac ;;
                --) shift ; break ;;
                *) echo "Error de tratamiento de opciones!" ; exit 1 ;;
        esac
done
¿Cómo usar getopts?

Getopts es algo similar, solo que suprime la evaluación y no hay bucle con condición true, la propia condición de while va recorriendo los elementos de la lista de argumentos con getopts. Es una construcción algo más sencilla de entender, aunque tiene menos posibilidades.
while getopts "ab:c::" OPCION
do
case $OPCION in
a)
ARGUMENTO1=true
;;
b)
ARGUMENTO2=true
OPT2=$OPTARG
;;
c)
ARGUMENTO3=true
if [ $OPTARG != "" ]; then
OPT3=$OPTARG
fi
;;
*)
echo "Opcion no reconocida"
exit 1
;;
?)
echo "Uso: ejemplo [-a] [-b opcionb] [-c [opcionc]]"
exit 1
;;
esac
done
¿Y después qué?

Getopt y getopts está implementado en bibliotecas para otros lenguajes de programación como C, PHP, Perl, Java. Lo más complicado será encontrar la forma de invocación y la manera en la que iremos paseándonos por los argumentos. El cómo especificar la cadena de argumentos es similar.

Artículo publicado para nomastec :: Jugando con getopt y getops

No hay comentarios:

Publicar un comentario