Cómo utilizar declaraciones de casos en scripts Bash

Clasificar formas en categorías en una pizarra
Patpitchaya / Shutterstock.com

Las declaraciones de casos de Bash son poderosas pero fáciles de escribir. Cuando vuelva a visitar un antiguo script de Linux, se alegrará de haber utilizado una casedeclaración en lugar de una if-then-elsedeclaración larga .

Declaración del caso

La mayoría de los lenguajes de programación tienen su versión de una declaración switcho case. Estos dirigen el flujo de ejecución del programa según el valor de una variable. Normalmente, hay una rama de ejecución definida para cada uno de los posibles valores esperados de la variable y una  rama general o  predeterminada para todos los demás valores.

La funcionalidad lógica es similar a una larga secuencia de if-thendeclaraciones con una elsedeclaración que captura todo lo que no ha sido manejado previamente por una de las ifdeclaraciones.

La implementación de Bash de case intenta hacer coincidir una  expresión  con una de las cláusulas. Lo hace mirando cada cláusula, a su vez, tratando de encontrar un patrón coincidente . Los patrones en las cláusulas son cadenas, pero, en contra de la intuición, eso no significa que no podamos usar valores numéricos como expresión.

El caso genérico

La forma genérica de la casedeclaración es la siguiente:

expresión de caso en 

  patrón-1)
    declaración 
    ;;

  patrón-2) 
    declaración
    ;;
    .
    .
    .

  patrón-N) 
    declaración 
    ;;

  *) 
    declaración 
    ;; 
esac

  • Una casedeclaración debe comenzar con la casepalabra clave y terminar con la esacpalabra clave.
  • La expresión se evalúa y se compara con los patrones de cada  cláusula  hasta que se encuentra una coincidencia.
  • Se ejecutan la declaración o declaraciones en la cláusula de coincidencia.
  • Se ;;utiliza un punto y coma doble » » para terminar una cláusula.
  • Si un patrón coincide y las declaraciones de esa cláusula se ejecutan, todos los demás patrones se ignoran.
  • No hay límite para el número de cláusulas.
  • Un asterisco “ *” indica el patrón predeterminado. Si una expresión no coincide con ninguno de los otros patrones de la casedeclaración, se ejecuta la cláusula predeterminada.
Relacionado:  Cómo comprobar qué versión de Android está en su Chromebook

Un ejemplo simple

Este guión nos dice el horario de apertura de una tienda imaginaria. Utiliza el datecomando con la +"%a"cadena de formato para obtener el nombre del día abreviado. Esto se almacena en la DayNamevariable.

#! / bin / bash

DayName = $ (fecha + "% a")

echo "Horario de apertura de $ DayName"

caso $ DayName en

  Lun)
    echo "09:00 - 17:30"
    ;;

  Mar)
    echo "09:00 - 17:30"
    ;;

  Casarse)
    echo "09:00 - 12:30"
    ;;

  Jue)
    echo "09:00 - 17:30"
    ;;

  Vie)
    echo "09:00 - 16:00"
    ;;

  Se sentó)
    echo "09:30 - 16:00"
    ;;

  Sol)
    echo "Cerrado todo el día"
    ;;

  *)
    ;;
esac

Copie ese texto en un editor y guárdelo como un archivo llamado «open.sh».

Necesitaremos usar el chmodcomando para hacerlo ejecutable. Deberá hacer eso para todos los scripts que cree mientras trabaja en este artículo.

chmod + x open.sh

Hacer ejecutable el script open.sh

Ahora podemos ejecutar nuestro script.

./open.sh

Ejecutando el script open.sh

El día en que se tomó la captura de pantalla es viernes. Eso significa que la DayName variable contiene la cadena «Vie». Esto se corresponde con el patrón «Vie» de la cláusula «Vie)».

Tenga en cuenta que los patrones en las cláusulas no necesitan estar entre comillas dobles, pero no hace ningún daño si lo están. Sin embargo, debe utilizar comillas dobles si el patrón contiene espacios.

La cláusula predeterminada se ha dejado vacía. Todo lo que no coincida con una de las cláusulas anteriores se ignora.

Ese guión funciona y es fácil de leer, pero es largo y repetitivo. Podemos acortar ese tipo de  case declaración con bastante facilidad.

Usar múltiples patrones en una cláusula

Una característica realmente interesante de las casedeclaraciones es que puede usar múltiples patrones en cada cláusula. Si la expresión coincide con alguno de esos patrones, se ejecutan las declaraciones de esa cláusula.

Aquí hay un guión que le dice cuántos días hay en un mes. Solo puede haber tres respuestas: 30 días, 31 días o 28 o 29 días para febrero. Entonces, aunque son 12 meses, solo necesitamos tres cláusulas.

En este script, se solicita al usuario el nombre de un mes. Para que la coincidencia de patrones no distinga entre mayúsculas y minúsculas, usamos el shoptcomando con la -s nocasematchopción. No importará si la entrada contiene mayúsculas, minúsculas o una combinación de las dos.

#! / bin / bash

shopt -s nocasematch

echo "Ingrese el nombre de un mes"
mes de lectura

caso $ mes en

  Febrero)
    echo "28/29 días en $ mes"
    ;;

  Abril | Junio ​​| Septiembre | Noviembre)
    echo "30 días en $ mes"
    ;;

  Enero | Marzo | Mayo | Julio | Agosto | Octubre | Diciembre)
    echo "31 días en $ mes"
    ;;

  *)
    echo "Mes desconocido: $ mes"
    ;;
esac

Febrero tiene una cláusula para sí mismo, y todos los demás meses comparten dos cláusulas según tengan 30 o 31 días en ellas. Las cláusulas de varios patrones utilizan el símbolo de barra vertical «|» como separador. El caso predeterminado captura meses con mala ortografía.

Relacionado:  Cómo sumar o restar fechas en Microsoft Excel

Guardamos esto en un archivo llamado «month.sh» y lo hicimos ejecutable.

chmod + x mes.sh

Ejecutaremos el script varias veces y mostraremos que no importa si usamos mayúsculas o minúsculas.

./month.sh

Ejecutando el script month.sh con diferentes entradas de caso

Debido a que le dijimos al script que ignorara las diferencias en mayúsculas y minúsculas, cualquier nombre de mes escrito correctamente es manejado por una de las tres cláusulas principales. Los meses mal escritos están sujetos a la cláusula predeterminada.

Uso de dígitos en declaraciones de casos

También podemos usar dígitos o variables numéricas como expresión. Este script le pide al usuario que ingrese un número en el rango 1..3. Para dejar en claro que los patrones de cada cláusula son cadenas, se han envuelto entre comillas dobles. A pesar de esto, el script aún hace coincidir la entrada del usuario con la cláusula apropiada.

#! / bin / bash

echo "Introduzca 1, 2 o 3:"
leer número

caso $ Número en

  "1")
    echo "Cláusula 1 coincidente"
    ;;

  "2")
    echo "Cláusula 2 coincidente"
    ;;

  "3")
    echo "Cláusula 3 coincidente"
    ;;

  *)
    echo "Cláusula predeterminada coincidente"
    ;;
esac

Guarde esto en un archivo llamado «number.sh», hágalo ejecutable y luego ejecútelo:

./number.sh

Ejecutando el script number.sh y probando diferentes entradas de usuario

Usar declaraciones de casos en bucles for

Una casedeclaración intenta hacer coincidir el patrón con una sola expresión. Si tiene muchas expresiones para procesar, puede poner la casedeclaración dentro de un forciclo.

Este script ejecuta el lscomando para obtener una lista de archivos. En el forbucle, se aplica globalización de archivos (similar pero diferente a las expresiones regulares) a cada archivo para extraer la extensión del archivo. Esto se almacena en la Extensionvariable de cadena.

La casedeclaración usa la Extensionvariable como la expresión que intenta hacer coincidir con una cláusula.

#! / bin / bash

para Archivo en $ (ls)

hacer
  # extraer la extensión del archivo
  Extensión = $ {Archivo ## *.}

  caso "$ Extension" en

    sh)
      echo "Secuencia de comandos de Shell: $ Archivo"
      ;;

    Maryland)
      echo "Archivo de Markdown: $ File"
      ;;

    png)
      echo "Archivo de imagen PNG: $ Archivo"
      ;;

    *)
      echo "Desconocido: $ Archivo"
      ;;
  esac
hecho

Guarde este texto en un archivo llamado «filetype.sh», hágalo ejecutable y luego ejecútelo usando:

./filetype.sh

Ejecución del script filetype.sh e identificación de archivos

Nuestro script de identificación de tipo de archivo minimalista funciona.

Relacionado:  Super Bowl 2022: las mejores ofertas de TV

Manejo de códigos de salida con declaraciones de casos

Un programa con buen comportamiento enviará un código de salida al shell cuando finalice. El esquema convencional usa un valor de código de salida de cero para indicar una ejecución sin problemas, y valores de uno o más para indicar diferentes tipos de error.

Muchos programas usan solo cero y uno. Agrupar todas las condiciones de error en un solo código de salida dificulta la identificación de problemas, pero es una práctica común.

Creamos un pequeño programa llamado «go-geek» que devolvería aleatoriamente códigos de salida de cero o uno. Este próximo script llama go-geek. Adquiere el código de salida usando la $?variable de shell y lo usa como expresión para la casedeclaración.

Un script del mundo real haría el procesamiento apropiado según el éxito o el fracaso del comando que generó el código de salida.

#! / bin / bash

go-geek

caso $? en

  "0")
    echo "La respuesta fue: Éxito"
    echo "Realice el procesamiento apropiado aquí"
    ;;

  "1")
    echo "La respuesta fue: Error"
    echo "Realice el manejo de errores apropiado aquí"
    ;;

  *)
    echo "Respuesta no reconocida: $?"
    ;;
esac

Guarde esto en un script llamado «return-code.sh» y hágalo ejecutable. Deberá sustituir nuestro go-geekcomando por algún otro comando. Puede intentar cdingresar a un directorio que no existe para obtener un código de salida de uno, y luego editar su secuencia de comandos cden un directorio accesible para obtener un código de salida de cero.

La ejecución del script varias veces muestra que la casedeclaración identifica correctamente los diferentes códigos de salida .

./return-code.sh

Ejecutando el script return-code.sh que muestra el manejo de diferentes códigos de salida

La legibilidad ayuda a la mantenibilidad

Volver a los antiguos scripts de Bash y averiguar cómo hacen lo que hacen, especialmente si fueron escritos por otra persona, es un desafío. Modificar la funcionalidad de los scripts antiguos es aún más difícil.

La casedeclaración le brinda una lógica de ramificación con una sintaxis clara y sencilla. Eso es beneficioso para todos.