Cómo analizar argumentos de línea de comandos en Bash

En este artículo, aprenderemos a manejar argumentos CLI en Bash. Comenzaremos leyendo argumentos posicionales y luego configuraciones más complejas con getopts

Introducción

Los scripts de Bash toman argumentos de la línea de comandos como entradas tanto secuencialmente como también, analizados como opciones. Las utilidades de la línea de comandos utilizan estos argumentos para activar condicionalmente funciones en un script de Bash o elegir selectivamente entre entornos para ejecutar el script. En Bash, estos se configuran de diferentes maneras.

En este artículo, exploraremos varias formas de crear estos argumentos para el consumo y demostraremos un ejemplo práctico para la implementación de argumentos de línea de comandos.

Argumentos posicionales de línea de comando

Los argumentos de la línea de comandos se leen de forma posicional, desde la posición $1, $2, ..$n. El patrón $ seguido de un entero es una combinación reservada para representar los argumentos de la línea de comandos.

{.icon aria-hidden=“true”}

$0 denota el nombre del script.

Aquí hay una representación pictórica de los argumentos posicionales de la línea de comandos utilizados para invocar un script:

Representación de argumentos de línea de comandos posicionales

Considere el siguiente script, arg_intro.sh, donde los argumentos se imprimen según sus posiciones:

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

echo "Script name is: $0"
echo "Arg1 is $1"
echo "Arg1 is $2"
echo "Arg1 is $3"
echo "-----------"
echo "All args: $*"
echo "All args count: $#"

Este script de shell se ejecuta con los argumentos que se muestran a continuación:

1
2
3
4
5
6
7
8
$ bash arg_intro.sh runtime inputs
Script name is: ./arg_intro.sh
Arg1 is runtime
Arg1 is inputs
Arg1 is 
-----------
All args: runtime inputs
All args count: 2

Análisis de argumentos complejos usando getopt

En la sección anterior, comprendió cómo se pasan los argumentos posicionales al script. Este método no funciona bien si hay un aumento en el número de argumentos o si hay un requisito condicional para la asignación de las variables. En tales casos, es necesario contar con un marco sólido.

La utilidad de línea de comandos getopt resuelve este problema proporcionando sintaxis y opciones para definir y analizar los argumentos.

Aquí hay una idea general sobre cómo definir argumentos usando getopt.

Definición de las opciones

Hay dos tipos de argumentos cuando se pasan a una utilidad de línea de comandos. Éstos incluyen:

  • Argumentos cortos: se definen con un solo carácter, precedido por un guión. Por ejemplo, -h puede denotar ayuda, -l puede denotar un comando lista.
  • Argumentos largos: estas son cadenas completas, precedidas por dos guiones. Por ejemplo, --help indica ayuda y --list indica lista.

Considere este script test_options.sh, donde configura los argumentos con la utilidad getopt:

1
2
3
4
5
#!/bin/bash

SHORT=c:,d:,h
LONG=city1:,city2:,help
OPTS=$(getopt --alternative --name weather --options $SHORT --longoptions $LONG -- "[correo electrónico protegido]") 

Los argumentos cortos se pasan al indicador --options de la utilidad getopt mientras que los argumentos largos se pasan al indicador --longoptions. En el código anterior, tenemos tres opciones cortas:

  • c corresponde a ciudad1
  • d corresponde a city2
  • h corresponde a ayuda.

La opción ayuda en las utilidades de la línea de comandos no toma ningún valor y, por lo tanto, no tiene dos puntos adjuntos.

  • Dos puntos (:) - Se requiere valor para esta opción
  • Doble dos puntos (::) - El valor es opcional
  • Sin dos puntos - No se requieren valores

En el código anterior, los argumentos c y d (seguidos de dos puntos) requieren que se envíe el valor, mientras que la opción h no requiere ningún argumento (sin dos puntos).

Cambiar a otros argumentos {#cambiar a otros argumentos}

La utilidad getopt pone los argumentos de entrada en una salida posicional organizada.

Por ejemplo, modifiquemos test_options.sh para imprimir nuestras opciones:

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

SHORT=c:,d:,h
LONG=city1:,city2:,help
OPTS=$(getopt -a -n weather --options $SHORT --longoptions $LONG -- "[correo electrónico protegido]")
echo $OPTS

Al pasar argumentos a este script, da como resultado el siguiente resultado:

1
2
$ bash test_options.sh --city1 Paris --city2 NewYork
--city1 'Paris' --city2 'NewYork' --

Aquí hay una representación visual del resultado generado por el script y una demostración de los entornos de control de calidad:

Representación de cómo funciona el desplazamiento de argumentos en las utilidades de línea de comandos

Tenga en cuenta que la utilidad ha agregado un guión doble final (--) que indica el final de la salida. Ahora, esta salida se puede tratar como argumentos posicionales, y los bucles se escriben para iterar a través de cada uno de ellos. Aquí es donde la palabra clave shift es útil.

La palabra clave shift toma un argumento adicional de cuántas posiciones de los argumentos para mover el cursor. Para analizar el siguiente argumento, el comando shift tiene asignado un número “2”, que cambiaría al siguiente argumento.

Considere el script test_options_looping.sh que usa shift para capturar e imprimir los valores de las opciones en nuestros argumentos:

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

SHORT=c:,d:,h
LONG=city1:,city2:,help
OPTS=$(getopt -a -n weather --options $SHORT --longoptions $LONG -- "[correo electrónico protegido]")

eval set -- "$OPTS"

while :
do
  case "$1" in
    -c | --city1 )
      city1="$2"
      shift 2
      ;;
    -d | --city2 )
      city2="$2"
      shift 2
      ;;
    -h | --help)
      "This is a weather script"
      exit 2
      ;;
    --)
      shift;
      break
      ;;
    *)
      echo "Unexpected option: $1"
      ;;
  esac
done

echo $city1, $city2

Cuando se llama al script pasando los argumentos válidos, da como resultado la impresión de las dos variables asignadas como:

1
2
$ bash test_options_looping.sh --city1 Paris --city2 NewYork
Paris, NewYork

Observe cómo se cerró el script cuando encontró un guión doble (--).

Con los aprendizajes de arriba, hagamos un ejemplo de aplicación meteorológica CLI que acepte y analice los nombres de las ciudades como argumentos.

Ejemplo: creación de una aplicación meteorológica de línea de comandos

La aplicación meteorológica de línea de comandos recupera datos de https://wttr.in, una aplicación web meteorológica pública y gratuita creada por Igor Chubin. Nuestro script de shell realiza una solicitud GET con la utilidad curl para recuperar un informe meteorológico. Si se pasan dos argumentos, da una comparación entre el clima en dos ciudades.

El script contiene todos los componentes mencionados anteriormente, incluida la llamada a la utilidad getopt. En función de los argumentos de entrada, se verifica el número de argumentos válidos.

Si no se pasan argumentos, la función help() se ejecuta y sale. Si no, la secuencia de comandos continúa y comprueba las opciones en el caso del cambio.

En función de las entradas, se evalúa la condición if-else y se realiza el curl de la URL reemplazando el argumento de cadena con entradas.

Vamos a crear un script llamado weather.sh con el siguiente 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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/bin/bash

help()
{
    echo "Usage: weather [ -c | --city1 ]
               [ -d | --city2 ]
               [ -h | --help  ]"
    exit 2
}

SHORT=c:,d:,h
LONG=city1:,city2:,help
OPTS=$(getopt -a -n weather --options $SHORT --longoptions $LONG -- "[correo electrónico protegido]")

VALID_ARGUMENTS=$# # Returns the count of arguments that are in short or long options

if [ "$VALID_ARGUMENTS" -eq 0 ]; then
  help
fi

eval set -- "$OPTS"

while :
do
  case "$1" in
    -c | --city1 )
      city1="$2"
      shift 2
      ;;
    -d | --city2 )
      city2="$2"
      shift 2
      ;;
    -h | --help)
      help
      ;;
    --)
      shift;
      break
      ;;
    *)
      echo "Unexpected option: $1"
      help
      ;;
  esac
done

if [ "$city1" ] && [ -z "$city2" ]
then
    curl -s "https://wttr.in/${city1}"
elif [ -z "$city1" ] && [ "$city2" ]
then
    curl -s "https://wttr.in/${city2}"
elif [ "$city1" ] && [ "$city2" ]
then
    diff -Naur <(curl -s "https://wttr.in/${city1}" ) <(curl -s "https://wttr.in/${city2}" )
else
    curl -s https://wttr.in
fi

El script se prueba con los siguientes casos:

i) Caso de prueba 1 - Cuando se proporciona una sola ciudad

1
$ bash weather.sh -city1 NewYork

Figura 1: Pasar el argumento city1 al script de shell

ii) Caso de prueba 2: cuando se proporcionan dos ciudades:

1
$ bash weather.sh -city1 NewYork -city2 NewDelhi

Figura 2: Pasar los argumentos city1 y city2 al script de shell

iii) Caso de prueba 3 - Cuando no se menciona ninguno de los argumentos de la ciudad:

1
$ bash weather.sh

Figure 3: Calling the shell script without any arguments

Como era de esperar, nuestro script funciona bien y devuelve el informe meteorológico para las ciudades de entrada. Cuando no se pasan argumentos, el script llama a la función help() para devolver la lista de argumentos admitidos.

Conclusión

En este artículo, hemos cubierto los conceptos básicos para pasar los argumentos opcionales complejos posicionales y de análisis desde un script de shell.

La utilidad de ejemplo que creamos llama a la API meteorológica en función de los argumentos que proporcionamos al script.