Cómo usar getopts para analizar las opciones de script de shell de Linux

Publicado: 2022-06-25
La pantalla de una computadora portátil que muestra el texto del terminal.
fatmawati achmad zaenuri/Shutterstock.com

¿Desea que sus scripts de shell de Linux manejen las opciones y los argumentos de la línea de comandos con más gracia? El getopts incorporado de Bash le permite analizar las opciones de la línea de comandos con delicadeza, y también es fácil. Te mostramos cómo.

Presentamos el getopts incorporado

Pasar valores a un script Bash es bastante sencillo. Llama a su secuencia de comandos desde la línea de comando o desde otra secuencia de comandos y proporciona su lista de valores detrás del nombre de la secuencia de comandos. Se puede acceder a estos valores dentro de su script como variables, comenzando con $1 para la primera variable, $2 para la segunda y así sucesivamente.

Pero si desea pasar opciones a un script, la situación rápidamente se vuelve más compleja. Cuando decimos opciones, nos referimos a las opciones, banderas o interruptores que pueden manejar programas como ls . Están precedidos por un guión " - " y generalmente actúan como un indicador para que el programa active o desactive algún aspecto de su funcionalidad.

Cómo usar el comando ls para listar archivos y directorios en Linux
RELACIONADO Cómo usar el comando ls para enumerar archivos y directorios en Linux

El comando ls tiene más de 50 opciones, principalmente relacionadas con el formato de su salida. La opción -X (ordenar por extensión) ordena la salida alfabéticamente por extensión de archivo. La opción -U (sin ordenar) enumera por orden de directorio.

Las opciones son solo eso, son opcionales. No sabe qué opciones, si las hay, elegirá usar el usuario, y tampoco sabe en qué orden puede enumerarlas en la línea de comando. Esto aumenta la complejidad del código necesario para analizar las opciones.

Las cosas se complican aún más si algunas de sus opciones toman un argumento, conocido como argumento de opción . Por ejemplo, la opción ls -w (ancho) espera ser seguida por un número, que representa el ancho máximo de visualización de la salida. Y, por supuesto, es posible que esté pasando otros parámetros a su secuencia de comandos que son simplemente valores de datos, que no son opciones en absoluto.

Afortunadamente getopts maneja esta complejidad por usted. Y debido a que está integrado, está disponible en todos los sistemas que tienen el shell Bash, por lo que no hay nada que instalar.

Nota: getopts No getopt

Hay una utilidad más antigua llamada getopt . Este es un pequeño programa de utilidad, no un incorporado. Hay muchas versiones diferentes de getopt con diferentes comportamientos, mientras que el getops incorporado sigue las pautas de POSIX.

 escriba getopts
 escriba getopt 

usando el comando type para ver la diferencia entre getop y getops

Debido a que getopt no está integrado, no comparte algunos de los beneficios automáticos que tiene getopts , como manejar los espacios en blanco con sensatez. Con getopts , el shell Bash ejecuta su script y el shell Bash realiza el análisis de opciones. No necesita invocar un programa externo para manejar el análisis.

La desventaja es que getopts no maneja nombres de opciones de formato largo con guiones dobles. Por lo tanto, puede usar opciones formateadas como -w pero no " ---wide-format ". Por otro lado, si tiene un script que acepta las opciones -a , -b y -c , getopts le permite combinarlas como -abc , -bca o -bac y así sucesivamente.

Estamos discutiendo y demostrando getopts en este artículo, así que asegúrese de agregar la "s" final al nombre del comando.

RELACIONADO: Cómo escapar de los espacios en las rutas de archivos en la línea de comandos de Windows

Un resumen rápido: manejo de valores de parámetros

Este script no usa opciones discontinuas como -a o -b . Acepta parámetros "normales" en la línea de comandos y se accede a ellos dentro del script como valores.

 #!/bin/bash

# obtener las variables una por una
echo "Variable Uno: $1" 
echo "Variable Dos: $2" 
echo "Variable Tres: $3"

# recorrer las variables
para var en " $@" hacer
echo "$ var" 
hecho

Se accede a los parámetros dentro del script como variables $1 , $2 o $3 .

Copie este texto en un editor y guárdelo como un archivo llamado "variables.sh". Tendremos que hacerlo ejecutable con el comando chmod . Deberá realizar este paso para todos los scripts que discutimos. Simplemente sustituya el nombre del archivo de script apropiado cada vez.

 chmod +x variables.sh 

usando el comando chmod para hacer un script ejecutable

Si ejecutamos nuestro script sin parámetros, obtenemos este resultado.

 ./variables.sh 

ejecutar un script sin parámetros

No pasamos parámetros, por lo que el script no tiene valores para informar. Proporcionemos algunos parámetros esta vez.

 ./variables.sh cómo geek 

ejecutar un script con tres palabras como parámetros

Como era de esperar, las variables $1 , $2 y $3 se han establecido en los valores de los parámetros y los vemos impresos.

Este tipo de manejo de parámetros uno por uno significa que necesitamos saber de antemano cuántos parámetros habrá. Al bucle en la parte inferior del script no le importa cuántos parámetros hay, siempre los recorre todos.

Si proporcionamos un cuarto parámetro, no se asigna a una variable, pero el ciclo aún lo maneja.

 ./variables.sh cómo geek sitio web 

pasar cuatro parámetros a un script que solo puede manejar tres

Si ponemos comillas alrededor de dos de las palabras, se tratan como un parámetro.

 ./variables.sh cómo "ser geek" 

citar dos parámetros de línea de comando para que se traten como un solo parámetro

Si vamos a necesitar que nuestro script maneje todas las combinaciones de opciones, opciones con argumentos y parámetros de tipo de datos "normales", necesitaremos separar las opciones de los parámetros regulares. Podemos lograr eso colocando todas las opciones, con o sin argumentos, antes de los parámetros regulares.

Pero no corramos antes de que podamos caminar. Veamos el caso más simple para manejar las opciones de la línea de comandos.

Opciones de manejo

Usamos getopts en un bucle while . Cada iteración del ciclo funciona en una opción que se pasó al script. En cada caso, la variable OPTION se establece en la opción identificada por getopts .

Con cada iteración del bucle, getopts pasa a la siguiente opción. Cuando no hay más opciones, getopts devuelve false y sale del ciclo while .

Cómo usar declaraciones de casos en scripts Bash
RELACIONADO Cómo usar declaraciones de casos en secuencias de comandos Bash

La variable OPTION se compara con los patrones en cada una de las cláusulas de declaración de caso. Debido a que estamos usando una declaración de caso, no importa en qué orden se proporcionen las opciones en la línea de comando. Cada opción se coloca en la declaración del caso y se activa la cláusula correspondiente.

Las cláusulas individuales en la declaración del caso facilitan la realización de acciones específicas de opciones dentro del script. Por lo general, en un script del mundo real, establecería una variable en cada cláusula, y estas actuarían como indicadores más adelante en el script, permitiendo o denegando alguna funcionalidad.

Copie este texto en un editor y guárdelo como un script llamado "options.sh" y hágalo ejecutable.

 #!/bin/bash

mientras getopts 'abc' OPCIÓN; hacer
  caso "$OPCIÓN" en 
    a) 
      echo "Opción a usada" ;;

    b)
      echo "Opción b usada"
      ;;

    C)
      echo "Opción c usada"
      ;;

    ?) 
      echo "Uso: $(nombre base $0) [-a] [-b] [-c]"
      salida 1
      ;;
  esac
hecho

Esta es la línea que define el ciclo while.

 mientras getopts 'abc' OPCIÓN; hacer

El comando getopts va seguido de la cadena de opciones . Esto enumera las letras que vamos a usar como opciones. Solo las letras de esta lista se pueden utilizar como opciones. Entonces, en este caso, -d no sería válido. Esto quedaría atrapado por la cláusula ?) porque getopts devuelve un signo de interrogación " ? ” para una opción no identificada. Si eso sucede, el uso correcto se imprime en la ventana de la terminal:

 echo "Uso: $(nombre base $0) [-a] [-b] [-c]"

Por convención, envolver una opción entre corchetes “ [] ” en este tipo de mensaje de uso correcto significa que la opción es opcional. El comando basename elimina cualquier ruta de directorio del nombre del archivo. El nombre del archivo de script se mantiene en $0 en los scripts de Bash.

Usemos este script con diferentes combinaciones de líneas de comandos.

 ./opciones.sh -a
 ./opciones.sh -a -b -c
 ./opciones.sh -ab -c
 ./opciones.sh -taxi 

probando un script que puede aceptar opciones de línea de comando de tipo interruptor

Como podemos ver, todas nuestras combinaciones de prueba de opciones se analizan y manejan correctamente. ¿Y si probamos una opción que no existe?

 ./opciones.sh -d 

El shell y el script informan de una opción no reconocida

Se activa la cláusula de uso, lo cual es bueno, pero también recibimos un mensaje de error del shell. Eso podría o no ser importante para su caso de uso. Si está llamando al script desde otro script que tiene que analizar mensajes de error, será más difícil si el shell también genera mensajes de error.

Desactivar los mensajes de error del shell es muy fácil. Todo lo que tenemos que hacer es poner dos puntos ” : ” como el primer carácter de la cadena de opciones.

Edite su archivo "options.sh" y agregue dos puntos como el primer carácter de la cadena de opciones, o guarde este script como "options2.sh" y hágalo ejecutable.

 #!/bin/bash

mientras que getopts ':abc' OPCIÓN; hacer
  caso "$OPCIÓN" en 
    a) 
      echo "Opción a usada" 
      ;;

    b)
      echo "Opción b usada"
      ;;

    C)
      echo "Opción c usada"
      ;;

    ?) 
      echo "Uso: $(nombre base $0) [-a] [-b] [-c]"
      salida 1
      ;;
  esac
hecho

Cuando ejecutamos esto y generamos un error, recibimos nuestros propios mensajes de error sin ningún mensaje de shell.

 ./opciones2.sh.sh -d 

Una opción no reconocida que se informa solo mediante secuencias de comandos

Uso de getopts con argumentos de opción

Para decirle a getopts que una opción será seguida por un argumento, coloque dos puntos ” : ” inmediatamente detrás de la letra de la opción en la cadena de opciones.

Si seguimos la "b" y la "c" en nuestra cadena de opciones con dos puntos, getopt esperará argumentos para estas opciones. Copie este script en su editor y guárdelo como "arguments.sh", y hágalo ejecutable.

Recuerde, los primeros dos puntos en la cadena de opciones se usan para suprimir los mensajes de error del shell; no tiene nada que ver con el procesamiento de argumentos.

Cuando getopt procesa una opción con un argumento, el argumento se coloca en la variable OPTARG . Si desea utilizar este valor en otra parte de su secuencia de comandos, deberá copiarlo en otra variable.

 #!/bin/bash

while getopts ':ab:c:' OPCIÓN; hacer

  caso "$OPCIÓN" en
    a)
      echo "Opción a usada"
      ;;

    b)
      argB="$OPTARG"
      echo "Opción b usada con: $argB"
      ;;

    C)
      argC="$OPTARG"
      echo "Opción c usada con: $argC"
      ;;

    ?)
      echo "Uso: $(nombre base $0) [-a] [-b argumento] [-c argumento]"
      salida 1
      ;;
  esac

hecho

Vamos a ejecutar eso y ver cómo funciona.

 ./argumentos.sh -a -b "cómo ser geek" -c reviewgeek
 ./argumentos.sh -c reviewgeek -a 

probando un script que puede manejar argumentos de opción

Así que ahora podemos manejar opciones con o sin argumentos, independientemente del orden en que se presenten en la línea de comandos.

Pero, ¿qué pasa con los parámetros regulares? Anteriormente dijimos que sabíamos que tendríamos que ponerlos en la línea de comando después de cualquier opción. A ver qué pasa si lo hacemos.

Opciones y parámetros de mezcla

Cambiaremos nuestro script anterior para incluir una línea más. Cuando el ciclo while haya salido y todas las opciones hayan sido manejadas, intentaremos acceder a los parámetros regulares. Imprimiremos el valor en $1 .

Guarde este script como "arguments2.sh" y hágalo ejecutable.

 #!/bin/bash

while getopts ':ab:c:' OPCIÓN; hacer

  caso "$OPCIÓN" en
    a)
      echo "Opción a usada"
      ;;

    b)
      argB="$OPTARG"
      echo "Opción b usada con: $argB"
      ;;

    C)
      argC="$OPTARG"
      echo "Opción c usada con: $argC"
      ;;

    ?)
      echo "Uso: $(nombre base $0) [-a] [-b argumento] [-c argumento]"
      salida 1
      ;;
  esac

hecho

echo "La variable uno es: $1"

Ahora probaremos algunas combinaciones de opciones y parámetros.

 ./argumentos2.sh dave
 ./argumentos2.sh -a dave
 ./argumentos2.sh -a -c cómo-ser-geek dave 

No se puede acceder a los parámetros estándar en un script que acepta argumentos de opción

Así que ahora podemos ver el problema. Tan pronto como se utilizan las opciones, las variables $1 en adelante se rellenan con los indicadores de opción y sus argumentos. En el último ejemplo, $4 mantendría el valor del parámetro "dave", pero ¿cómo accede a eso en su secuencia de comandos si no sabe cuántas opciones y argumentos se van a usar?

La respuesta es usar OPTIND y el comando shift .

El comando shift descarta el primer parámetro, independientemente del tipo, de la lista de parámetros. Los otros parámetros se “rebajan aleatoriamente”, por lo que el parámetro 2 se convierte en el parámetro 1, el parámetro 3 se convierte en el parámetro 2, y así sucesivamente. Y así, $2 se convierte en $1 , $3 se convierte en $2 , y así sucesivamente.

Si proporciona shift con un número, eliminará esa cantidad de parámetros de la lista.

OPTIND cuenta las opciones y los argumentos a medida que se encuentran y procesan. Una vez que se hayan procesado todas las opciones y argumentos, OPTIND será uno más que el número de opciones. Entonces, si usamos shift para recortar (OPTIND-1) parámetros de la lista de parámetros, nos quedaremos con los parámetros regulares en $1 en adelante.

Eso es exactamente lo que hace este script. Guarde este script como "arguments3.sh" y hágalo ejecutable.

 #!/bin/bash

while getopts ':ab:c:' OPCIÓN; hacer
  caso "$OPCIÓN" en
    a)
      echo "Opción a usada"
      ;;

    b)
      argB="$OPTARG"
      echo "Opción b usada con: $argB"
      ;;

    C)
      argC="$OPTARG"
      echo "Opción c usada con: $argC"
      ;;

    ?)
      echo "Uso: $(nombre base $0) [-a] [-b argumento] [-c argumento]"
      salida 1
      ;;
  esac
hecho

echo "Antes - la variable uno es: $1"
cambio "$(($OPTIND -1))"
echo "Después - la variable uno es: $1"
echo "El resto de los argumentos (operandos)"

para x en "$@"
hacer
  eco $x
hecho

Lo ejecutaremos con una combinación de opciones, argumentos y parámetros.

 ./arguments3.sh -a -c how-to-geek "dave dee" dozy beaky mick tich 

Acceder correctamente a los parámetros estándar en un script que acepta argumentos de opción

Podemos ver que antes de llamar shift , $1 contenía "-a", pero después del comando de cambio, $1 contiene nuestro primer parámetro que no es una opción ni un argumento. Podemos recorrer todos los parámetros tan fácilmente como en un script sin análisis de opciones.

Siempre es bueno tener opciones

El manejo de opciones y sus argumentos en scripts no tiene por qué ser complicado. Con getopts puede crear secuencias de comandos que manejen las opciones, los argumentos y los parámetros de la línea de comandos exactamente como deberían hacerlo las secuencias de comandos nativas compatibles con POSIX.