Publi

Procesar argumentos de entrada en nuestros shell scripts con getopt

mixer_4

Hace unos días analizamos cómo tratar los argumentos de entrada desde un shell script en Bash de una manera sencilla. Aunque, cuando la cosa se complica, debemos utilizar herramientas algo más avanzadas. Tal y como hicimos con getopt para C [parte 1, parte 2], vamos a hacer lo mismo en un shell script.

Aunque aquí tenemos dos posibilidades, que hacen prácticamente lo mismo getopt y getopts. Vamos a verlas detenidamente.

El programa de ejemplo

En este ejemplo, vamos a imaginar que el script hace una copia de seguridad. Dicha copia de seguridad, tendrá varios argumentos de entrada:

  • -v : Escribe en pantalla todo lo que está haciendo en cada momento
  • -l [archivo] : Escribe un log en el archivo
  • -z : Comprime la copia de seguridad
  • -c : Copia remotamente el backup a un servidor
  • -h : Servidor donde vamos a copiar el backup

Además, debemos decirle los directorios que vamos a copiar. Al menos uno será obligatorio. Además, si se especifica -c, será también obligatorio especificar -h.

getopts, terminado en s

Es el más sencillo, básicamente el uso de getopts lo haremos para no complicarnos la vida cuando un argumento tiene parámetro o no, ya que no tenemos que hacer shift como pasaba en la parte 1 de este tutorial.
Veamos un fragmento de código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash

VERBOSE=0
COPIAREMOTA=0

while getopts vzcl:h: option; do
  case $option in
    v)
      VERBOSE=1
      ;;
    z)
      COMPRIMIDO=1
      ;;
    c)
      COPIAREMOTA=1
      ;;
    l)
      LOGFILE=$OPTARG
      ;;
    h)
      HOSTCOPY=$OPTARG
      ;;
  esac
done

if [ $COPIAREMOTA -eq 1 ] && [ -z "$HOSTCOPY" ]
then
    echo "Si especifica copia remota -c DEBE especificar también host (-h)"
    exit 1
fi

shift $(( OPTIND - 1 ));
if [ $# -le 0 ]
then
    echo "Por favor, especifique los directorios a copiar"
    exit 1
fi

echo "VERBOSE: $VERBOSE"
echo "COMPRIMIDO: $COMPRIMIDO"
echo "LOGFILE: $LOGFILE"
echo "COPIA REMOTA: $COPIAREMOTA"
echo "HOSTCOPY: $HOSTCOPY"
echo "Directorios a copiar: $@"

El formato de utilización de getopts es:

getopts FORMATO VARIABLE [ARGS]

donde,

  • FORMATO será una cadena donde especificaremos los argumentos que serán flags, es decir, los que no necesitan nada más, y los que necesitan un segundo argumento para definir su funcionalidad. Estos últimos, vendrán acompañados de dos puntos (:).
  • VARIABLE será la variable de nuestro shell script donde almacenaremos el argumento que está analizándose ahora mismo
  • ARGS, si se especifica, indica de dónde se van a sacar los argumentos. Por defecto son los parámetros posicionales $1, $2, $3…

Lo bueno de utilizar getopts y no utilizar los métodos de la primera parte del tutorial es, además, que podemos combinar varios argumentos de entrada en un mismo argumento físico. Dicho de otra forma, no tenemos por qué llamar al programa así:

./test -v -z -c -h SERVIDOR directorio1, directorio2, …, directorioN

podemos hacerlo así:

./test vzch SERVIDOR directorio1, directorio2, …, directorioN

y esto lo hace mucho más amigable al usuario.

El gran problema de getopts, es que si los directorios a copiar los ponemos al principio, o incluso en medio, esos argumentos no se parsearán, y tal vez el comportamiento del programa no sea el deseado.

getopt, sin s, el más flexible

Pero claro, si queremos que nuestro programa acepte argumentos largos (–verbose para -v ; –zip para -z ; –copy para -c ; –log para -l ; –host para -h ) y, además, no queremos tener el problema de que el resto de argumentos se especifiquen en cualquier lugar (al principio, en medio o al final), la solución es getopt, sin s. Con esta utilidad, lo malo es que tenemos que andarnos con los shift de nuevo, pero yo creo que la flexibilidad que nos brinda compensa ese pequeño dolor.

Veamos un ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/bin/bash

ARGS=$(getopt -q -o "vzcl:h:" -l "verbose,zip,copy,log:,host:" -n "argumentos" -- "$@");

if [ $? -ne 0 ];
then
    echo "Ha habido un error al parsear los argumentos"
    exit 1
fi

eval set -- "$ARGS";

VERBOSE=0
COPIAREMOTA=0

while [ $# -gt 0 ]; do
  case "$1" in
    -v|--verbose)
      VERBOSE=1
      ;;
    -z|--zip)
      COMPRIMIDO=1
      ;;
    -c|--copy)
      COPIAREMOTA=1
      ;;
    -l|--log)
      LOGFILE="$2"
      shift;
      ;;
    -h|--host)
      HOSTCOPY="$2"
      shift;
      ;;
    --)
      shift;
      break;
      ;;
  esac
  shift
done

if [ $COPIAREMOTA -eq 1 ] && [ -z "$HOSTCOPY" ]
then
    echo "Si especifica copia remota -c DEBE especificar también host (-h)"
    exit 1
fi

if [ $# -le 0 ]
then
    echo "Por favor, especifique los directorios a copiar"
    exit 1
fi

echo "VERBOSE: $VERBOSE"
echo "COMPRIMIDO: $COMPRIMIDO"
echo "LOGFILE: $LOGFILE"
echo "COPIA REMOTA: $COPIAREMOTA"
echo "HOSTCOPY: $HOSTCOPY"
echo "Directorios a copiar: $@"

Viendo esto, getopt, en realidad lo que hace es reordenar los argumentos, es decir, los argumentos que estén juntos (por ejemplo -cvzl) los separa (quedando -c -v -z -l), para que los podamos analizar mejor. Además, los argumentos que no tengan ninguna clave primero, vamos los que quedan sueltos, los pone al final, después de un último argumento «–» (dos guiones, WordPress me pone un guión sólo y me da cosa quitarlo)

Entonces, podemos analizar los argumentos desde la salida de getopt, no sin antes pasar esa variable a los argumentos posicionales (con eval set — «$ARGS»). Cuando estemos analizando, es importante marcar un fin al while, como en el ejemplo, no encontrar más argumentos de entrada, aunque si encontramos «–» hacemos un break y salimos, pero nunca se sabe lo que puede pasar en las salidas, o si un IFS se nos va de madre…

O si preferimos, podemos hacerlo con un for sobre los mismos argumentos, y olvidarnos del case, aunque también tiene sus contras, si queremos extraer los archivos sueltos, debemos hacerlo de otra forma también:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#!/bin/bash

ARGX=($(getopt -q -o "vzcl:h:" -l "verbose,zip,copy,log:,host:" -n "argumentos" -- "$@"));

if [ $? -ne 0 ];
then
    echo "Ha habido un error al parsear los argumentos"
    exit 1
fi

VERBOSE=0
COPIAREMOTA=0

for (( arg=0; $arg<$# ; arg++ ))
do
  case "${ARGX[$arg]}" in
    -v|--verbose)
      VERBOSE=1
      ;;
    -z|--zip)
      COMPRIMIDO=1
      ;;
    -c|--copy)
      COPIAREMOTA=1
      ;;
    -l|--log)
      ((arg++))
      LOGFILE="${ARGX[$arg]}"
      ;;
    -h|--host)
      ((arg++))
      HOSTCOPY="${ARGX[$arg]}"
      ;;
    --)
      ULTIMO=$arg+1;
      break;
      ;;
  esac
done

if [ $COPIAREMOTA -eq 1 ] && [ -z "$HOSTCOPY" ]
then
    echo "Si especifica copia remota -c DEBE especificar también host (-h)"
    exit 1
fi

if [ $# -le 0 ]
then
    echo "Por favor, especifique los directorios a copiar"
    exit 1
fi

echo "VERBOSE: $VERBOSE"
echo "COMPRIMIDO: $COMPRIMIDO"
echo "LOGFILE: $LOGFILE"
echo "COPIA REMOTA: $COPIAREMOTA"
echo "HOSTCOPY: $HOSTCOPY"
echo "Directorios a copiar: "
for ((dir=$ULTIMO; $dir<$#; dir++))
do
    echo ${ARGX[dir]}
done

Extra, getopts en KornShell


En Twitter, Ingenieria.inversa() nos envía su versión para KornShell utilizando getopts. Aquí tenéis el código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/ksh
if [ $# == 9 ]
then
    exit 1
else
    for op in `echo "$@"`
    do
        if [[ $op == -* ]] && [[ $op != -[hmps] ]]
        then
            OPS="$OPS $op"
        fi
        export OPS
    done
    if [ -n "$OPS" ]
    then
        if [ ${#OPS} -gt 3 ]
        then
            printf "\n ERROR:\tLos paranetros $OPS no son validos.\n"
        else
            printf "\n ERROR:\tE1 paranetro $OPS no es valido.\n"
        fi
        uso
        exit 1
    else
        while getopts hm:p:s: flag
        do
            case $flag in
                h) echo "BlablabIa..." ;;
                m) VAR1="$2" ;;
                p)
                    case $4 in
                        "uno") VAR2="$4" ;;
                        "dos") VAR2="$4" ;;
                        "tres") VAR2="$4" ;;
                        *) exit 1 ;;
                    esac
                    ;;
                s) VAR3="$6" ;;
                *) exit 2 ;;
            esac
        done
        shift $(($OPTIND -1))
    fi
fi

Para los curiosos

Algo que podemos probar es que, estas herramientas no están limitadas a los argumentos de entrada al script. Pueden ser argumentos de entrada de una función de Bash, o incluso podremos especificar en una cadena de caracteres lo que queremos que getopt o getopts parsee.

Más info

Getopts tutorial
Command line options
Foto principal: freestocks.org

También podría interesarte....

There are 22 comments left Ir a comentario

  1. Pingback: Procesar argumentos de entrada en nuestros shell scripts con getopt | PlanetaLibre /

  2. Pingback: Script para realizar capturas de pantalla rápidas con las opciones que necesito – Poesía Binaria /

  3. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    This is the ultimate destination for anyone seeking the thrill of the biggest web slots. สล็อตเว็บใหญ่ สุด

  4. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Are there any reviews on durable slot options available? สล็อตเว็บตรง แตกง่าย

  5. PeterAdams /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    And i’m glad reading your article. But should remark on some general things, The web site style is perfect, the articles is really great : D. Good job, cheers internet providers Edmonton

  6. sisa /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    What a great accomplishment! เว็บคาสิโน ไม่ผ่านเอเย่นต์

  7. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Recorded here you’ll learn it is imperative, them offers the connection in an accommodating page: เว็บรวมสล็อตทุกค่าย

  8. SimonWhitehead /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Thanks for this great post, i find it very interesting and very well thought out and put together. I look forward to reading your work in the future. Ergonomic Office Chair

  9. SimonWhitehead /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    You there, this is really good post here. Thanks for taking the time to post such valuable information. Quality content is what always gets the visitors coming. office furniture

  10. maree /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    You always make me see things in a better way.คาสิโนวอเลท

  11. SimonWhitehead /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    What is meant by eagle scout applications in the first place? I was hoping to find some information on the subject on https://www.airslate.com/workflows/document/hobbies-and-interests, but that wasn’t forthcoming. Hopefully, I’ll find some other information.What is meant by eagle scout applications in the first place? I was hoping to find some information on the subject on https://www.airslate.com/workflows/document/hobbies-and-interests, but that wasn’t forthcoming. Hopefully, I’ll find some other information.

  12. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    I no uncertainty esteeming each and every bit of it. It is an amazing site and superior to anything normal give. I need to grateful. Marvelous work! Every one of you complete an unfathomable blog, and have some extraordinary substance. Keep doing stunning. สล็อตโรม่า

  13. jsimitseo /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    I’ve legitimate chosen to assemble a blog, which I hold been inadequate to improve the situation an amid. Recognizes for this advise, it’s extremely serviceable! Pokdeng

  14. vunawer /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Some SMM panels offer white label solutions, allowing resellers to brand the services as their own. This means that end clients may not be aware of the original source of the services.
    SMM Panel

  15. tahrey /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    Se você está interessado em comprar um mini porco comprar algumas orientações que podem ajudá-lo a encontrar um mini porco de estimação.
    mini porco comprar

  16. SimonWhitehead /
    Usando Google Chrome Google Chrome 120.0.0.0 en Windows Windows NT

    My comfort comes from a website called https://www.signnow.com/esignature/how-to-esign-a-pdf. I literally got everything in my hands in record time. Highly recommended!

  17. jsimitseo /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    Incredible post I should state and much obliged for the data. Instruction is unquestionably a sticky subject. Be that as it may, is still among the main themes of our opportunity. I value your post and anticipate more. concierge doctor naples

  18. SimonWhitehead /
    Usando Google Chrome Google Chrome 121.0.0.0 en Windows Windows NT

    hi was just seeing if you minded a comment. i like your website and the thme you picked is super. I will be back. tradesviz avis

  19. SimonWhitehead /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    Thank you for your very good information and feedback from you. san jose car dealers click me

  20. seo /
    Usando Google Chrome Google Chrome 122.0.0.0 en Windows Windows NT

    thanksssssssss hggjhjhk

  21. seoo /
    Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    I think this is an informative post and it is very useful and knowledgeable. immediate ai urex website

  22. seo /
    Usando Google Chrome Google Chrome 123.0.0.0 en Windows Windows NT

    Admiring the time and effort you put into your blog and detailed information you offer!..Great post, and great website. Thanks for the information! Thanks for all your help and wishing you all the success in your business. daman games

Leave a Reply