case
El comando compuesto de bash para múltiples opciones se llama case. Tiene la siguiente sintaxis:
case palabra in
[patrón [| patrón]...) comandos ;;]...
esac
Si miramos el programa read-menu del Capítulo 28, vemos la lógica usada para actuar en una selección del usuario:
#!/bin/bash
# read-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -p "Enter selection [0-3] > "
if [[ $REPLY =~ ^[0-3]$ ]]; then
if [[ $REPLY == 0 ]]; then
echo "Program terminated."
exit
fi
if [[ $REPLY == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
exit
fi
if [[ $REPLY == 2 ]]; then
df -h
exit
fi
if [[ $REPLY == 3 ]]; then
if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
exit
fi
else
echo "Invalid entry." >&2
exit 1
fi
Usando case podemos reemplazar esta lógica con algo más simple:
#!/bin/bash
if [[ $REPLY == 0 ]]; then
echo "Program terminated."
exit
fi
if [[ $REPLY == 1 ]]; then
echo "Hostname: $HOSTNAME"
uptime
exit
fi
if [[ $REPLY == 2 ]]; then
df -h
exit
fi
if [[ $REPLY == 3 ]]; then
if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
exit
fi
else
echo "Invalid entry." >&2
exit 1
fi
Usando case podemos reemplazar esta lógica con algo más simple:
#!/bin/bash
# case-menu: a menu driven system information program
clear
echo "
Please Select:
1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
0. Quit
"
read -p "Enter selection [0-3] > "
case $REPLY in
0) echo "Program terminated."
exit
;;
1) echo "Hostname: $HOSTNAME"
uptime
;;
2) df -h
;;
3) if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esac
El comando case mira el valor de palabra, en nuestro ejemplo, el valor de la variable REPLY, y luego trata de compararlo con uno de los patrones especificados. Cuando encuentra una coincidencia, los comandos asociados con el patrón especificado se ejecutan. Una vez que se encuentra una coincidencia, no se intentan más coincidencias.
Patrones
Los patrones usados por case son los mismos que aquellos usados por la expansión de rutas. Los patrones terminan con un carácter ")". Aquí tenemos algunos patrones válidos:
Ejemplos de patrones de case
a)
Coincide si palabra es igual a "a".
[[:alpha:]])
Coincide si palabra es un caracter alfabético único.
???)
Coincide si palabra tiene exactamente tres caracteres de longitud.
*.txt)
Coincide si palabra finaliza con los caracteres ".txt".
*)
Coincide con cualquier valor de palabra. Es una buena práctica incluirlo como el último patrón en un comando case, para tomar cualquier valor de palabra que no coincida con un patrón previo; o sea, para tomar todos los valores no válidos posibles.
Aquí tenemos un ejemplo de cómo funcionan los patrones:
#!/bin/bash
Aquí tenemos un ejemplo de cómo funcionan los patrones:
#!/bin/bash
read -p "enter word > "
case $REPLY in
[[:alpha:]]) echo "is a single alphabetic character." ;;
[ABC][0-9]) echo "is A, B, or C followed by a digit." ;;
???) echo "is three characters long." ;;
*.txt) echo "is a word ending in '.txt'" ;;
*) echo "is something else." ;;
esac
También es posible combinar múltiples patrones usando el carácter de la barra vertical como separador. Esto crea un patrón de "o" condicional. Esto es útil para cosas como manejar caracteres tanto mayúsculas como minúsculas. Por ejemplo:
#!/bin/bash
# case-menu: a menu driven system information program
clear
echo "
Please Select:
A. Display System Information
B. Display Disk Space
C. Display Home Space Utilization
Q. Quit
"
read -p "Enter selection [A, B, C or Q] > "
case $REPLY in
q|Q) echo "Program terminated."
exit
;;
a|A) echo "Hostname: $HOSTNAME"
uptime
;;
b|B) df -h
;;
c|C) if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esac
Aquí, modificamos el programa case-menu para usar letras en lugar de dígitos para las selecciones del menú. Fíjate cómo los nuevos patrones permiten introducir tanto letras mayúsculas como minúsculas.
q|Q) echo "Program terminated."
exit
;;
a|A) echo "Hostname: $HOSTNAME"
uptime
;;
b|B) df -h
;;
c|C) if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esac
Aquí, modificamos el programa case-menu para usar letras en lugar de dígitos para las selecciones del menú. Fíjate cómo los nuevos patrones permiten introducir tanto letras mayúsculas como minúsculas.
Realizando múltiples acciones
En versiones de bash anteriores a la 4.0, case permite que se realice una única acción en una coincidencia exitosa. Tras una coincidencia exitosa, el comando terminaría. Aquí vemos un script que prueba un carácter:
#!/bin/bash
# case4-1: test a character
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]]) echo "'$REPLY' is upper case." ;;
[[:lower:]]) echo "'$REPLY' is lower case." ;;
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;
[[:digit:]]) echo "'$REPLY' is a digit." ;;
[[:graph:]]) echo "'$REPLY' is a visible character." ;;
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;
esac
Ejecutar este script produce esto:
[me@linuxbox ~]$ case4-1
Type a character > a
'a' is lower case.
El script funciona en su mayor parte, pero falla si un carácter coincide en más de una clase de caracter POSIX. Por ejemplo, el carácter "a" es tanto mayúscula como minúscula, así como un dígito hexadecimal. En bash anterior a la versión 4.0 no hay forma de que case coincida en más de una prueba. Las versiones modernas de bash, añaden la notación ";;&" para terminar cada acción, así que podemos hacer esto:
#!/bin/bash
# case4-2: test a character
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esac
Cuando ejecutamos este script, obtenemos esto:
[me@linuxbox ~]$ case4-2
Type a character > a
'a' is lower case.
'a' is alphabetic.
'a' is a visible character.
'a' is a hexadecimal digit.
Añadir la sintaxis ";;&" permite que case continúe con el siguiente test en lugar de simplemente terminar.
Resumiendo
El comando case es un añadido útil en nuestra maleta llena de trucos de programación. Como veremos en el próximo capítulo, es la herramienta perfecta para manejar ciertos tipos de problemas.
Para saber más
- La sección de Estructuras Condicionales del Manual de Referencia de Bashdescribe el comando case al detalle:
http://tiswww.case.edu/php/chet/bash/bashref.html#SEC21 - La Guía Avanzada de Scripting en Bash ofrece más ejemplos de aplicaciones de case:
http://tldp.org/LDP/abs/html/testbranch.html
No hay comentarios:
Publicar un comentario
Nota: solo los miembros de este blog pueden publicar comentarios.