Publi

Cómo procesar múltiples argumentos de entrada en scripts para Bash

photo-1455540904194-fc101941273a_r

Los argumentos de entrada de un programa nos sirven para modificar ligeramente el comportamiento de un programa dentro de las opciones que nos permita su desarrollador. Y, sobre todo, siempre que trabajamos con programas para consola, es muy buena idea permitir que nuestros usuarios puedan utilizar funciones extras o especificar sus propios parámetros de trabajo sin necesidad de modificar el programa.

Si trabajáis con lenguajes como C o C++ podéis leer algo de información al respecto:

Pero, cuando hacemos algún script en Bash para línea de comandos, también puede resultar muy útil, tanto para especificar archivos de entrada, como las propias opciones de nuestro programa. Dicha gestión de argumentos la podemos hacer de varias maneras, dependiendo de lo avanzadas que sean nuestras necesidades.

Acceso básico a los argumentos de entrada

Como ocurre con las funciones, podemos acceder a los argumentos de entrada con $n siendo n el número del argumento que queremos pedir. Y no nos dará error si pedimos un argumento inexistente. Por lo que si hacemos este pequeño script:

1
2
3
4
5
6
7
#!/bin/bash

echo "Primero: $0"
echo "Segundo: $1"
echo "Tercero: $2"
echo "Cuarto:  $3"
echo "Quinto:  $4"

Y si lo ejecutamos de la siguiente forma, tendremos una salida parecida a esta:

$ bash argbash.sh escribo argumentos “para mi” programa
Primero: argbash.sh
Segundo: escribo
Tercero: argumentos
Cuarto: para mi
Quinto: programa

Nota: también podemos dar permisos de ejecución a argbash.sh y no hace falta llamar a bash primero.
Como siempre, en la línea de comandos, los argumentos vienen separados por espacios, pero si escribimos el argumento entre comillas, en ese sí que podremos escribir espacios.

Con esto y un poco manejo de condicionales, ya podremos tener un manejo básico de los argumentos de entrada de nuestro programa.

Contar argumentos del programa

1
2
3
#!/bin/bash

echo "Has pasado $# argumentos"

Así podemos saber cuántos argumentos nos han pasado, en el ejemplo anterior, devolvería 4. Hemos visto que existe $0, el cual indica el nombre de nuestro ejecutable (que también podemos jugar con él, hacer una condición dependiendo del nombre del ejecutable para correr un código u otro).
Pero en sí $# contará el número de argumentos propiamente dichos (no como en C, por ejemplo, en el que el argumento 0 sí que cuenta).

Comprobar argumento vacío

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

if [ -z "$3" ]
then
    echo "El tercer argumento está vacío"
fi

if [ -n "$2" ]
then
    echo "El segundo argumento tiene datos"
fi

Con estas líneas podemos comprobar si el tercer argumento existe o no (o al menos que esté vacío) y justo lo contrario con el segundo, es decir, ejecutaremos código cuando el segundo argumento tenga datos.

Iterando los argumentos

Pero vamos, lo interesante empieza ahora. Podemos hacer bucles que recorran los argumentos de entrada de nuestro programa, por ejemplo:

1
2
3
4
5
6
7
8
9
#!/bin/bash

CONTADOR=0

for i in "$@"
do
    let CONTADOR=$CONTADOR+1
    echo "Argumento $CONTADOR: $i"
done

La parte del contador no es estrictamente necesaria, pero da un toque de distinción. Desde aquí hemos recorrido todos los argumentos del programa, ya podremos procesarlos como queramos, o si son archivos de entrada, abrirlos, etc.

Pero también podemos hacerlo de otra forma, que nos puede resultar más cómoda en otras ocasiones, con un bucle while:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

CONTADOR=0

while [ "$*" ]
do
    let CONTADOR=$CONTADOR+1
    echo "Argumento $CONTADOR: $1"
    shift
done

De esta forma, dentro del while estamos preguntando: ¿hay argumentos? Si es así, aumentamos contador y mostramos el argumento 1. Luego llamamos a shift, que básicamente se carga el primer argumento desplazando todos los demás un lugar hacia la izquierda, es decir, el segundo ahora es el primero, el tercero ahora es el segundo y así sucesivamente.

Este tipo de bucles, nos ayudarán para el siguiente ejemplo, en el que puede ser que en alguna iteración debamos extraer más de un argumento y, aunque sería posible hacerlo con el for, complicaría un poco la lógica.

Parsear opciones de entrada

Una de los puntos fuertes es que podemos recorrer los argumentos de entrada de un programa para definir sus opciones. Por ejemplo, si nuestro programa acepta (es sólo un ejemplo, nuestro programa podrá aceptar cualquier argumento):

  • -h o –help : ayuda
  • -u o –user : introducir nombre de usuario
  • -s o –server : nombre del servidor
  • -v o –verbose : escribir en pantalla todos los mensajes

Podremos hacer un ejemplo como este:

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
#!/bin/bash

function showhelp()
{
    echo "Ayuda del programa"
    exit 0
}

# Mientras el número de argumentos NO SEA 0
while [ $# -ne 0 ]
do
    case "$1" in
    -h|--help)
        # No hacemos nada más, porque showhelp se saldrá del programa
        showhelp
        ;;
    -u|--user)
        USERNAME="$2"
        shift
        ;;
    -s|--server)
        SERVER="$2"
        shift
        ;;
    -v|--verbose)
        VERBOSE=1
        ;;
    *)
        echo "Argumento no válido"
        showhelp
        ;;
    esac
    shift
done

if [ -z "$USERNAME" ]
then
    echo "El nombre de usuario es OBLIGATORIO"
fi

if [ -z "$SERVER" ]
then
    SERVER="servidor.por.defecto.com"
fi

echo "Nombre de usuario: $USERNAME"
echo "Servidor: $SERVER"
if [ -n "$VERBOSE" ]
then
    echo "El modo VERBOSE está ACTIVADO"
else
    echo "El modo VERBOSE está DESACTIVADO"
fi

Como vemos, hemos utilizado un case para que, en cada iteración del while, analicemos el argumento actual y en consecuencia tomemos decisiones. En este caso, cuando el argumento es sencillo, como -v, le damos un valor a la variable VERBOSE y terminamos, cuando termina case, hay un shift, así que corremos los argumentos una posición y seguimos en el bucle. Pero cuando el argumento acepta un parámetro adicional, es decir “-u USUARIO”, debemos leer tanto “-u” como “USUARIO”, por eso en esa parte del case, asignamos el segundo argumento a la variable USERNAME y hacemos un shift, que acompañado al shift que hay detrás del case, hará que corramos los argumentos dos posiciones.

Para el siguiente post

Para no alargar mucho este post, he decidido cortar aquí para una segunda parte. Esa segunda parte tratará de cómo hacer esto mismo utilizando getopt, una utilidad que nos hará tener un uso un poco más avanzado de los argumentos de un programa y que, incluso puede tener utilidades muy interesantes.

Foto principal: freestocks.org

También podría interesarte....

Leave a Reply