Control de flujo

Sentencia if

En shell scripting (Bash y similares), los comandos if, else, elif, junto con los operadores lógicos (and, or, not), se utilizan para implementar estructuras de control de flujo. El comando exit se usa para finalizar un script o un proceso con un código de salida. A continuación, te explico cómo funcionan:


1. Condicionales (if, else, elif):

Los condicionales se usan para ejecutar bloques de código dependiendo de si una condición es verdadera o falsa.

Sintaxis básica:

if [ condición ]; then
    # Código a ejecutar si la condición es verdadera
elif [ otra_condición ]; then
    # Código a ejecutar si la otra condición es verdadera
else
    # Código a ejecutar si ninguna condición es verdadera
fi

Ejemplo:

#!/bin/bash

num=10

if [ $num -gt 5 ]; then
    echo "El número es mayor que 5"
elif [ $num -eq 5 ]; then
    echo "El número es igual a 5"
else
    echo "El número es menor que 5"
fi

2. Operadores lógicos (and, or, not):

En Bash, los operadores lógicos no se escriben directamente como and, or, o not. En su lugar, usamos:

OperadorSímboloDescripción
AND&&Ambas condiciones deben ser verdaderas.
OR`
NOT!Niega una condición.

Ejemplo con AND y OR:

#!/bin/bash

a=10
b=20

if [ $a -gt 5 ] && [ $b -gt 15 ]; then
    echo "Ambas condiciones son verdaderas"
elif [ $a -gt 5 ] || [ $b -lt 15 ]; then
    echo "Al menos una condición es verdadera"
else
    echo "Ninguna condición es verdadera"
fi

Ejemplo con NOT:

#!/bin/bash

a=5

if ! [ $a -eq 10 ]; then
    echo "El valor de a no es 10"
fi

3. El comando exit:

El comando exit se utiliza para terminar un script o un proceso con un código de salida. Un código de salida:

  • 0 significa que el script se ejecutó con éxito.
  • Cualquier otro número (por convención, 1-255) indica un error o estado específico.

Ejemplo básico:

#!/bin/bash

if [ $# -eq 0 ]; then
    echo "No se pasaron argumentos"
    exit 1  # Termina el script con un código de error
fi

echo "Argumentos recibidos: $@"
exit 0  # Termina con éxito

Cuando ejecutas un script, puedes pasarle argumentos desde la línea de comandos. $# te indica cuántos de esos argumentos se pasaron.


Resumen del flujo:

Un ejemplo más completo:

#!/bin/bash

read -p "Introduce un número: " num

if [ $num -gt 10 ]; then
    echo "El número es mayor que 10"
elif [ $num -eq 10 ]; then
    echo "El número es igual a 10"
else
    echo "El número es menor que 10"
fi

if [ $num -gt 5 ] && ! [ $num -eq 7 ]; then
    echo "El número es mayor que 5 pero no es 7"
fi

exit 0  # Salida exitosa

Variable especial $?

En Bash scripting, $? es una variable especial que contiene el código de salida del último comando o script que se ejecutó.


¿Qué es el código de salida?

  • 0: Indica que el comando o script anterior se ejecutó correctamente (éxito).
  • Cualquier otro valor: Indica que hubo un error o estado específico. Por convención:
    • Valores positivos (como 1, 2, etc.) indican errores o condiciones específicas.
    • Los valores exactos dependen del comando que se ejecutó.

Uso básico de $?:

  1. Después de un comando:

    ls /ruta/valida
    echo "Código de salida: $?"
    
    ls /ruta/inexistente
    echo "Código de salida: $?"
    

    Salida:

    Código de salida: 0   # La primera ejecución de `ls` fue exitosa.
    Código de salida: 2   # La ruta no existe, por lo tanto, `ls` falló.
    
  2. En un script:

    #!/bin/bash
    
    mkdir nueva_carpeta
    if [ $? -eq 0 ]; then
        echo "Carpeta creada con éxito."
    else
        echo "Error al crear la carpeta."
    fi
    

Cómo utilizar $? en diferentes escenarios:

1. Verificar el éxito de un comando:

cp archivo.txt /directorio/
if [ $? -eq 0 ]; then
    echo "Archivo copiado correctamente."
else
    echo "Error al copiar el archivo."
fi

2. Condicionales encadenados:

comando_1
if [ $? -ne 0 ]; then
    echo "comando_1 falló, terminando el script."
    exit 1
fi

comando_2
echo "comando_2 ejecutado correctamente."

Combinación con exit:

Puedes usar $? para devolver el estado de salida de un comando dentro de un script.

Ejemplo:

#!/bin/bash

ls /directorio/inexistente
exit $?  # El script devolverá el código de salida del comando anterior.

Ejecución:

$ bash mi_script.sh
$ echo $?
2  # El código de salida de `ls` se propagó al script.

Ejemplo completo:

#!/bin/bash

echo "Probando comandos:"
mkdir carpeta_test

if [ $? -eq 0 ]; then
    echo "Se creó la carpeta correctamente."
else
    echo "Error al crear la carpeta."
fi

rm -r carpeta_test
if [ $? -eq 0 ]; then
    echo "Se eliminó la carpeta correctamente."
else
    echo "Error al eliminar la carpeta."
fi

Resumen:

  • $? es útil para verificar el resultado de comandos y scripts.
  • Es especialmente importante en scripts automatizados para manejar errores y asegurarte de que las operaciones se ejecuten como esperas. ¡Te da control total sobre el flujo del script! 🎯

Explicación del Comando test y el Uso de [] en Shell Script

En Shell Script, el comando test y los corchetes [] son herramientas fundamentales para realizar evaluaciones de condiciones, como verificar si un archivo existe, comparar números, o evaluar cadenas de texto.


¿Qué es test?

test es un comando interno del shell que evalúa una condición y devuelve un código de salida:

  • 0 (verdadero) si la condición se cumple.
  • 1 (falso) si no se cumple.

Sintaxis básica:

test EXPRESIÓN

Por ejemplo:

test -f archivo.txt

Esto verifica si archivo.txt existe y es un archivo regular.


¿Qué son los corchetes []?

Los corchetes [] son una sintaxis simplificada de test. Internamente, [ EXPRESIÓN ] es equivalente a test EXPRESIÓN. Sin embargo, hay que dejar un espacio entre los corchetes y la expresión.

Sintaxis básica:

[ EXPRESIÓN ]

Por ejemplo:

[ -f archivo.txt ]

Hace lo mismo que test -f archivo.txt.


Casos de uso comunes

1. Evaluar cadenas de texto

  • Verificar si una cadena no está vacía:

    [ -n "$cadena" ] && echo "No está vacía"
    

    Equivalente a:

    test -n "$cadena" && echo "No está vacía"
    
  • Verificar si dos cadenas son iguales:

    [ "$cadena1" = "$cadena2" ] && echo "Son iguales"
    

2. Comparar números

  • Comparar si un número es mayor que otro:

    [ "$num1" -gt "$num2" ] && echo "$num1 es mayor"
    
  • Verificar si dos números son iguales:

    [ "$num1" -eq "$num2" ] && echo "Son iguales"
    

3. Verificar archivos y directorios

  • Verificar si un archivo existe:

    [ -e archivo.txt ] && echo "El archivo existe"
    
  • Verificar si es un directorio:

    [ -d /ruta/directorio ] && echo "Es un directorio"
    
  • Verificar si un archivo tiene permisos de lectura:

    [ -r archivo.txt ] && echo "Tiene permisos de lectura"
    

Ejemplo práctico

Supongamos que queremos verificar si un archivo llamado config.txt existe y tiene permisos de escritura antes de editarlo:

#!/bin/bash

archivo="config.txt"

if [ -e "$archivo" ]; then
  if [ -w "$archivo" ]; then
    echo "El archivo $archivo existe y tiene permisos de escritura."
  else
    echo "El archivo $archivo existe pero no tiene permisos de escritura."
  fi
else
  echo "El archivo $archivo no existe."
fi

Diferencia entre test y []

Ambos funcionan de manera similar, pero [] es más común por ser más fácil de leer. Sin embargo, si usas test, no tienes que preocuparte por los espacios extras que [] requiere.


¿Qué hay de [[ ]]?

[[ ]] es una versión mejorada y más flexible de [] disponible en Bash. Admite:

  • Comparaciones regulares (como =~ para expresiones regulares).
  • Evita errores en comparaciones de cadenas vacías.

Ejemplo con expresiones regulares:

cadena="hello123"
[[ $cadena =~ ^hello[0-9]+$ ]] && echo "Coincide con el patrón"

Conclusión

  • Usa test o [] para condiciones simples.
  • Usa [[ ]] para scripts más complejos en Bash.
  • Siempre ten cuidado con los espacios en [], ya que olvidarlos puede generar errores.

Estas herramientas son esenciales en Shell Script para tomar decisiones dinámicas según las condiciones del sistema o del usuario.

Cuando usas un if con una función dentro de [], el comportamiento depende de cómo se evalúa el código de salida de la función y cómo [] maneja este resultado.


Funcionamiento general de if

En Bash, if evalúa el código de salida de un comando o función. Por convención:

  • Un código de salida 0 indica éxito (true).
  • Un código de salida distinto de 0 indica error (false).

Ejemplo básico:

my_function() {
  return 3
}

if my_function; then
  echo "True"
else
  echo "False"
fi

Salida:

False

Esto ocurre porque my_function devuelve un código de salida 3 (no 0), lo que if interpreta como falso.


Uso de [] en un if

Cuando encapsulas una función o comando dentro de []:

  • [ no evalúa el código de salida directamente. En su lugar, evalúa el contenido como una expresión.
  • Si la expresión dentro de [] no está vacía, se considera true.
  • El código de salida de la función no afecta directamente la evaluación.

Ejemplo:

my_function() {
  return 3
}

if [ my_function ]; then
  echo "True"
else
  echo "False"
fi

Salida:

True

Esto sucede porque [ my_function ] no ejecuta la función. En su lugar, evalúa si el texto "my_function" no está vacío (y cualquier texto no vacío es considerado true).


Caso en que [] ejecuta la función

Si incluyes una ejecución explícita de la función dentro de [], el resultado sigue sin depender del código de salida de la función, sino de cómo se interpreta la expresión.

Ejemplo:

my_function() {
  return 3
}

if [ $(my_function) ]; then
  echo "True"
else
  echo "False"
fi

Salida:

False

Aquí, $(my_function) ejecuta la función, pero como no produce una salida, el comando dentro de [] evalúa vacío (false).


Diferencias clave entre [] y evaluar directamente la función

CasoResultado del ifMotivo
if my_functionBasado en returnEvalúa directamente el código de salida de la función (0 = true, distinto de 0 = false).
if [ my_function ]Siempre true[ evalúa si "my_function" (una cadena no vacía) es true.
if [ $(my_function) ]Basado en salidaEvalúa el resultado de ejecutar la función. Si no genera salida, es false.

Conclusión

  • Sin []: el if usa directamente el código de salida de la función.
  • Con []: no considera el código de salida de la función; evalúa si el contenido dentro de los corchetes es true o false como una expresión.
  • [ $(my_function) ] sirve para compara salidas en echo con texto si hacemos un return siembre sera falso
#!/bin/bash

# Nombre del archivo a buscar
archivo="mi_archivo.txt"

# Ruta donde buscar el archivo (puedes cambiar "." por otra ruta)
ruta="."

# Comando find: verifica si existe un archivo con nombre $archivo modificado en los últimos 30 minutos
if find "$ruta" -name "$archivo" -type f -mmin -30 | grep -q "$archivo"; then
  echo "El archivo '$archivo' ya existe y fue modificado en los últimos 30 minutos."
else
  echo "El archivo '$archivo' no existe o no fue modificado recientemente. Creándolo..."
  touch "$ruta/$archivo"
fi

Expresiones para comando test

CategoríaExpresiónVerdadero si…
Númerosinteger1 -eq integer2integer1 es igual a integer2.
integer1 -ne integer2integer1 no es igual a integer2.
integer1 -le integer2integer1 es menor o igual a integer2.
integer1 -lt integer2integer1 es menor que integer2.
integer1 -ge integer2integer1 es mayor o igual a integer2.
integer1 -gt integer2integer1 es mayor que integer2.
Cadenas de textostringLa cadena no está vacía (string no es null).
-n stringLa longitud de string es mayor a cero.
-z stringLa longitud de string es igual a cero.
string1 = string2string1 y string2 son iguales.
string1 == string2string1 y string2 son iguales.
string1 != string2string1 y string2 no son iguales.
Archivosfile1 -ef file2file1 y file2 tienen el mismo número de inodo (hard link).
file1 -nt file2file1 es más reciente que file2.
file1 -ot file2file1 es más antiguo que file2.
-b fileEl archivo file existe y es un archivo especial de bloques (block-special).
-c fileEl archivo file existe y es un archivo especial de caracteres.
-d fileEl archivo file existe y es un directorio.
-e fileEl archivo file existe.
-f fileEl archivo file existe y es un archivo regular.
-k fileEl archivo file existe y tiene el “sticky bit” activado.
-L fileEl archivo file existe y es un enlace simbólico.
-s fileEl archivo file existe y tiene longitud mayor a cero.
-u fileEl archivo file existe y tiene el bit setuid activado.
-w fileEl archivo file existe y es escribible (por el usuario efectivo).
-x fileEl archivo file existe y es ejecutable (por el usuario efectivo).

Ejemplo de uso

Evaluando números:

a=5
b=10
if [ $a -lt $b ]; then
  echo "a es menor que b"
else
  echo "a no es menor que b"
fi

Evaluando cadenas:

cadena="Hola"
if [ -n "$cadena" ]; then
  echo "La cadena no está vacía"
else
  echo "La cadena está vacía"
fi

Evaluando archivos:

if [ -f "/path/to/file.txt" ]; then
  echo "El archivo existe"
else
  echo "El archivo no existe"
fi

Sentencia if: Condiciones avanzadas

Esto hace perder a bash retrocompatibilidad ya que solo funciona en bash moderno

Expresiones avanzadas con if usando [[ ]], wildcards y expresiones regulares en Bash

En Bash, las construcciones [[ ]] se utilizan para realizar evaluaciones condicionales más avanzadas que las que se pueden hacer con los tradicionales test o [ ]. Cuando usamos [[ ]], obtenemos una sintaxis más poderosa que permite trabajar con wildcards, expresiones regulares y otras características. En este artículo, exploraremos cómo usar [[ ]] con wildcards y expresiones regulares, y cómo aprovecharlos en condiciones if para crear scripts más eficientes.

¿Qué son los wildcards en Bash?

Los wildcards (comodines) son caracteres que representan uno o más caracteres en cadenas de texto. Los más comunes son:

  • *: Coincide con cualquier número de caracteres (incluido ninguno).
  • ?: Coincide con un solo carácter.
  • []: Coincide con cualquier carácter dentro de los corchetes.

Ejemplo de wildcards:

# Coincide con cualquier archivo que empiece con "archivo" y termine con ".txt"
ls archivo*.txt

Sintaxis avanzada con [[ ]]

El uso de [[ ]] permite evaluaciones condicionales con una sintaxis más flexible que el comando test. Dentro de [[ ]], se pueden usar comodines (*, ?, []), además de otras funcionalidades que no están disponibles con test.

1. Uso de wildcards con [[ ]]

En un bloque if, puedes usar wildcards directamente dentro de las condiciones para evaluar patrones de coincidencia. Esto es útil para verificar si un archivo o una cadena de texto coincide con un patrón determinado.

Ejemplo 1: Coincidencia con * (cualquier número de caracteres)

#!/bin/bash

filename="documento.txt"

if [[ $filename == documento*.txt ]]; then
  echo "El archivo es un documento de texto"
else
  echo "El archivo no es un documento de texto"
fi

En este caso, el patrón documento*.txt coincide con cualquier archivo que empiece con “documento” y termine con “.txt”. Si filename coincide con este patrón, la condición será verdadera.

Ejemplo 2: Coincidencia con ? (un solo carácter)

#!/bin/bash

word="archivo1"

if [[ $word == archivo? ]]; then
  echo "La palabra tiene 7 caracteres y termina en un dígito"
else
  echo "La palabra no cumple con el patrón"
fi

Aquí, el ? indica que cualquier único carácter puede seguir a “archivo”, lo que significa que el patrón coincidirá con cadenas como “archivo1”, “archivoA”, etc.

Ejemplo 3: Coincidencia con [] (un conjunto de caracteres)

#!/bin/bash

filename="documento1"

if [[ $filename == documento[0-9] ]]; then
  echo "El archivo tiene un número al final"
else
  echo "El archivo no tiene un número al final"
fi

El patrón documento[0-9] coincide con cualquier cadena que empiece con “documento” y tenga un solo dígito del 0 al 9 al final.

2. Uso de expresiones regulares con [[ ]]

Otra característica poderosa de [[ ]] es que soporta expresiones regulares, lo que permite realizar coincidencias más complejas que con los simples wildcards. Dentro de [[ ]], podemos usar la opción =~ para evaluar expresiones regulares.

Ejemplo 4: Coincidencia con una expresión regular

#!/bin/bash

string="El número es 1234"

if [[ $string =~ [0-9]{4} ]]; then
  echo "La cadena contiene un número de 4 dígitos"
else
  echo "La cadena no contiene un número de 4 dígitos"
fi

En este ejemplo, la expresión regular [0-9]{4} busca un número de exactamente 4 dígitos dentro de la cadena. Si la cadena contiene este patrón, la condición es verdadera.

Ejemplo 5: Uso de expresiones regulares con caracteres especiales

#!/bin/bash

email="usuario@example.com"

if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$ ]]; then
  echo "El correo electrónico es válido"
else
  echo "El correo electrónico no es válido"
fi

En este caso, estamos utilizando una expresión regular para validar un correo electrónico. La expresión ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$ verifica si el formato del correo electrónico es correcto, es decir, si tiene el formato usuario@dominio.com.

3. Combinando condiciones con [[ ]]

También puedes combinar múltiples condiciones dentro de un [[ ]], usando operadores lógicos como && (AND) o || (OR).

Ejemplo 6: Uso de && (AND)

#!/bin/bash

string="Hola123"

if [[ $string =~ [A-Za-z]+ && $string =~ [0-9]+ ]]; then
  echo "La cadena contiene letras y números"
else
  echo "La cadena no contiene letras y números"
fi

En este ejemplo, la condición es verdadera si la cadena contiene tanto letras como números.

Ejemplo 7: Uso de || (OR)

#!/bin/bash

filename="documento.txt"

if [[ $filename == *.txt || $filename == *.pdf ]]; then
  echo "El archivo es de tipo texto o PDF"
else
  echo "El archivo no es de tipo texto o PDF"
fi

Aquí, el archivo es considerado de tipo texto o PDF si su extensión es .txt o .pdf.

Resumen

  • [[ ]]: Permite evaluaciones condicionales más poderosas que [ ] y test, con soporte para wildcards y expresiones regulares.
  • Wildcards: Se usan dentro de [[ ]] para hacer coincidir patrones como *, ? y [].
  • Expresiones regulares: Usando =~, puedes realizar coincidencias con patrones complejos.
  • Operadores lógicos: Se pueden combinar varias condiciones con && (AND) o || (OR) dentro de [[ ]].

Uso de (( )) en Bash

El operador (( )) en Bash es utilizado para realizar operaciones aritméticas, pero también puede ser utilizado para realizar comparaciones y trabajar con variables numéricas de manera eficiente. Se trata de una estructura de evaluación aritmética avanzada que permite realizar cálculos, comparaciones y asignaciones sin necesidad de utilizar el comando expr o herramientas externas.

¿Qué hace (( ))?

Dentro de (( )), puedes realizar operaciones aritméticas como suma, resta, multiplicación, división, y más. Además, permite realizar comparaciones numéricas y asignaciones de una manera mucho más fácil y rápida que con otras alternativas en Bash.

Características clave de (( )):

  • Operaciones aritméticas: Permite realizar operaciones como suma, resta, multiplicación, división y módulo.
  • Comparaciones: Facilita la comparación de valores numéricos utilizando operadores como -eq, -ne, -gt, -lt, etc., pero con una sintaxis más simple.
  • Asignaciones: Puedes asignar resultados de operaciones aritméticas directamente a variables dentro de (( )).

Sintaxis básica

(( expression ))

La expresión dentro de (( )) es evaluada como una operación aritmética. Si la expresión es verdadera (no es igual a cero), el resultado de la evaluación será “exit status 0” (verdadero). Si la expresión es falsa (igual a cero), el resultado será “exit status 1” (falso).

Operaciones aritméticas comunes

1. Suma (+)

#!/bin/bash

a=5
b=3
(( sum = a + b ))
echo $sum  # Imprime 8

2. Resta (-)

#!/bin/bash

a=10
b=4
(( diff = a - b ))
echo $diff  # Imprime 6

3. Multiplicación (*)

#!/bin/bash

a=6
b=7
(( product = a * b ))
echo $product  # Imprime 42

4. División (/)

#!/bin/bash

a=10
b=2
(( quotient = a / b ))
echo $quotient  # Imprime 5

5. Módulo (resto de la división) (%)

#!/bin/bash

a=10
b=3
(( remainder = a % b ))
echo $remainder  # Imprime 1

Comparaciones numéricas dentro de (( ))

Dentro de (( )), se pueden realizar comparaciones utilizando los operadores estándar de Bash:

  • -eq: Igual a (equal)
  • -ne: No igual a (not equal)
  • -gt: Mayor que (greater than)
  • -lt: Menor que (less than)
  • -ge: Mayor o igual que (greater than or equal to)
  • -le: Menor o igual que (less than or equal to)

Sin embargo, dentro de (( )), no es necesario usar el prefijo - para las comparaciones, ya que estos operadores están simplificados:

  • ==: Igual
  • !=: No igual
  • >: Mayor que
  • <: Menor que
  • >=: Mayor o igual que
  • <=: Menor o igual que

Ejemplos de comparaciones dentro de (( ))

1. Comparación de igualdad (==)

#!/bin/bash

a=5
b=5
if (( a == b )); then
  echo "a es igual a b"
else
  echo "a no es igual a b"
fi

2. Comparación de desigualdad (!=)

#!/bin/bash

a=5
b=3
if (( a != b )); then
  echo "a no es igual a b"
else
  echo "a es igual a b"
fi

3. Comparación mayor que (>)

#!/bin/bash

a=10
b=5
if (( a > b )); then
  echo "a es mayor que b"
else
  echo "a no es mayor que b"
fi

4. Comparación menor que (<)

#!/bin/bash

a=3
b=7
if (( a < b )); then
  echo "a es menor que b"
else
  echo "a no es menor que b"
fi

5. Comparación mayor o igual que (>=)

#!/bin/bash

a=8
b=8
if (( a >= b )); then
  echo "a es mayor o igual que b"
else
  echo "a no es mayor o igual que b"
fi

6. Comparación menor o igual que (<=)

#!/bin/bash

a=6
b=10
if (( a <= b )); then
  echo "a es menor o igual que b"
else
  echo "a no es menor o igual que b"
fi

Usando (( )) con variables no numéricas

Aunque (( )) está diseñado para trabajar con números, también puedes usarlo con expresiones que evalúan condiciones verdaderas o falsas en función de su valor. Si la variable contiene un valor no numérico, Bash interpretará su valor en términos de 0 (falso) o cualquier valor distinto de cero (verdadero).

Ejemplo 1: Uso con booleanos

#!/bin/bash

a=5
if (( a )); then
  echo "a es un valor verdadero"
else
  echo "a es un valor falso"
fi

Aquí, como a es un número distinto de cero, la condición es verdadera.

Resumen

  • (( )) se utiliza para realizar operaciones aritméticas y comparaciones numéricas.
  • Dentro de (( )), los operadores aritméticos son más simples que en el caso de [ ] o test.
  • Puedes usar (( )) para hacer comparaciones numéricas sin el prefijo -, como ==, !=, >, <, >=, <=.
  • Es posible realizar asignaciones dentro de (( )), lo que lo hace más cómodo que usar expr o comandos externos.
  • Es ideal para condiciones donde se necesita evaluar operaciones matemáticas y comparaciones numéricas.

El uso de (( )) simplifica muchas operaciones en Bash y hace que los scripts sean más fáciles de leer y escribir cuando se trata de cálculos y comparaciones numéricas.

AND , OR y NOT con [] y [[]]

En Bash, tanto [ como [[ son utilizados para evaluar condiciones dentro de una estructura if. Sin embargo, hay diferencias clave en cómo se manejan las expresiones lógicas como AND, OR y NOT cuando se utilizan [ (conocido como test) y [[ (conocido como double brackets). A continuación, te explico cómo funcionan estas expresiones lógicas en ambos casos:

1. AND (&&):

El operador AND devuelve verdadero si ambas condiciones son verdaderas. Se usa de manera diferente según utilices [ o [[.

Con [:

Para hacer un AND con [ (test), debes encadenar dos condiciones usando -a (que significa “AND”). A continuación se muestra un ejemplo de cómo se utilizaría:

if [ $a -eq 5 -a $b -eq 10 ]; then
  echo "Ambas condiciones son verdaderas"
fi

Nota: El operador -a funciona solo con [ y no se recomienda su uso en expresiones complejas debido a limitaciones.

Con [[:

Con [[, el operador AND se expresa utilizando &&. Esto es mucho más flexible y sencillo de usar, especialmente con cadenas o expresiones más complejas. Aquí tienes un ejemplo de cómo usarlo:

if [[ $a -eq 5 && $b -eq 10 ]]; then
  echo "Ambas condiciones son verdaderas"
fi

2. OR (||):

El operador OR devuelve verdadero si al menos una de las condiciones es verdadera. Similar al AND, se usa de manera diferente con [ y [[.

Con [:

Para hacer un OR con [ (test), debes usar el operador -o (que significa “OR”). Aquí tienes un ejemplo:

if [ $a -eq 5 -o $b -eq 10 ]; then
  echo "Al menos una de las condiciones es verdadera"
fi

Nota: Al igual que con -a, el uso de -o no siempre es ideal para expresiones más complejas.

Con [[:

Con [[, el operador OR se expresa utilizando ||, que es más intuitivo y versátil. Un ejemplo sería:

if [[ $a -eq 5 || $b -eq 10 ]]; then
  echo "Al menos una de las condiciones es verdadera"
fi

3. NOT (!):

El operador NOT invierte el valor lógico de una condición. Si la condición es verdadera, se vuelve falsa, y si es falsa, se vuelve verdadera.

Con [:

Para usar NOT con [ (test), puedes usar el operador ! al principio de la condición. Aquí tienes un ejemplo:

if [ ! $a -eq 5 ]; then
  echo "La condición no es verdadera"
fi

Con [[:

Con [[, se usa también ! de forma similar para negar una condición. El uso con [[ puede ser más legible y manejar cadenas con mayor facilidad. Aquí tienes un ejemplo:

if [[ ! $a -eq 5 ]]; then
  echo "La condición no es verdadera"
fi

Resumen: Comparación entre [ y [[

OperadorCon [Con [[
AND-a&&
OR-o`
NOT!!

Ventajas de [[ sobre [:

  • Mayor flexibilidad: [[ permite expresiones regulares, no requiere que las variables estén entre comillas, y maneja cadenas con espacios o caracteres especiales más fácilmente.
  • Mejor manejo de errores: [[ no tiene problemas con el espacio y las comillas en variables, mientras que [ requiere un manejo más cuidadoso.

En general, se recomienda usar [[ cuando sea posible debido a su mayor facilidad de uso y flexibilidad.

Comando exit

En Bash, los comandos return y exit se utilizan para salir de diferentes contextos, y entender sus diferencias es clave para escribir scripts efectivos. Aquí te explico sus funciones y diferencias con ejemplos prácticos.


1. return

Uso:

  • Se utiliza dentro de funciones en Bash para salir de la función y devolver un valor de estado.
  • El valor devuelto por return puede ser utilizado por el código que llama a la función para tomar decisiones.
  • Solo puede devolver un valor numérico (entre 0 y 255).

Ejemplo:

#!/bin/bash

my_function() {
  echo "Dentro de la función"
  return 42
}

my_function
echo "Código de salida de la función: $?"  # $? contiene el código de salida del último comando.

Salida:

Dentro de la función
Código de salida de la función: 42

En este ejemplo, return termina la ejecución de la función y proporciona un código de salida que puede ser capturado con $?.


2. exit

Uso:

  • Se utiliza para terminar completamente el script en cualquier punto.
  • También puede devolver un valor numérico que indica el estado de salida del script al sistema operativo.
  • A diferencia de return, exit no está limitado al contexto de funciones; finaliza todo el proceso del script.

Ejemplo:

#!/bin/bash

echo "Inicio del script"

exit 1  # Termina el script inmediatamente con código de salida 1.

echo "Esto nunca se ejecutará"

Salida:

Inicio del script

El código posterior a exit no se ejecutará porque finaliza todo el script.


Diferencias Clave

Aspectoreturnexit
Contexto de usoSolo dentro de funciones.En cualquier parte del script.
EfectoSale de la función.Termina completamente el script.
PropósitoIndicar el resultado de la función.Indicar el estado final del script.
Código de salidaDevuelve un valor numérico al llamador de la función.Devuelve un valor numérico al sistema operativo.
Ejecución posteriorContinúa ejecutando el script.No ejecuta el código posterior.

3. Ejemplo Comparativo

A continuación, se muestra un ejemplo donde se usan ambos comandos para entender mejor cómo funcionan en un script:

#!/bin/bash

my_function() {
  echo "Dentro de la función"
  if [[ $1 -gt 10 ]]; then
    echo "Valor demasiado alto, saliendo de la función"
    return 1  # Sale de la función con código de error 1
  fi
  echo "Valor aceptable"
}

echo "Inicio del script"
my_function 15

if [[ $? -ne 0 ]]; then
  echo "Error detectado en la función, terminando el script"
  exit 1  # Termina el script con código de error 1
fi

echo "Fin del script"

Salida cuando se pasa 15:

Inicio del script
Dentro de la función
Valor demasiado alto, saliendo de la función
Error detectado en la función, terminando el script

En este caso:

  • return finaliza solo la función si se pasa un valor mayor a 10.
  • exit se usa para finalizar el script por completo si la función devuelve un error.

Resumen de Uso

  • Usa return cuando solo necesitas salir de una función y devolver un código de salida para control de flujo.
  • Usa exit cuando necesitas terminar completamente el script, generalmente indicando éxito (exit 0) o error (exit 1 o superior).

Ambos comandos son esenciales para controlar el flujo de un script, pero es importante elegir el adecuado según el contexto.

Bucle for

El bucle for en Shell Script es una herramienta fundamental para iterar sobre listas de elementos, como palabras, números, resultados de comandos, archivos o directorios. Su simplicidad y flexibilidad lo hacen ideal para realizar tareas repetitivas.

A continuación, te explico en detalle cómo funciona el bucle for en Shell Script.


1. Sintaxis básica

for variable in lista
do
  # comandos
done
  • variable: Toma cada valor de lista en cada iteración.
  • lista: Puede ser una lista explícita de valores, un rango, una salida de comando, o archivos.
  • Los comandos dentro del bloque do...done se ejecutan una vez por cada valor en lista.

2. Ejemplo básico

#!/bin/bash

for color in rojo verde azul
do
  echo "El color es $color"
done

Salida:

El color es rojo
El color es verde
El color es azul

Aquí, color toma los valores rojo, verde y azul en cada iteración.


3. Iteración sobre un rango de números

El bucle for puede iterar sobre un rango numérico utilizando {inicio..fin}:

#!/bin/bash

for num in {1..5}
do
  echo "Número: $num"
done

Salida:

Número: 1
Número: 2
Número: 3
Número: 4
Número: 5

3.1 Con pasos específicos

Podemos definir un incremento o decremento con {inicio..fin..paso}:

#!/bin/bash

for num in {10..1..2}
do
  echo "Número: $num"
done

Salida:

Número: 10
Número: 8
Número: 6
Número: 4
Número: 2

4. Iteración sobre archivos en un directorio

El bucle for es muy útil para procesar archivos o directorios:

#!/bin/bash

for file in *.txt
do
  echo "Procesando archivo: $file"
done

Salida (si hay archivos .txt en el directorio):

Procesando archivo: archivo1.txt
Procesando archivo: archivo2.txt

En este ejemplo, *.txt expande a todos los archivos con esa extensión en el directorio actual.


5. Iteración sobre la salida de un comando

Puedes usar el resultado de un comando como la lista de elementos:

#!/bin/bash

for user in $(cat usuarios.txt)
do
  echo "Usuario: $user"
done

Salida (si usuarios.txt contiene nombres de usuarios):

Usuario: Juan
Usuario: Maria
Usuario: Pedro

Nota sobre espacios:

Si los elementos tienen espacios, es mejor usar un while con un read, ya que $(...) divide por espacios. Para manejar esto correctamente, usa:

#!/bin/bash

while IFS= read -r user; do
  echo "Usuario: $user"
done < usuarios.txt

6. Anidar bucles for

Puedes usar bucles for dentro de otros para iterar sobre estructuras más complejas:

#!/bin/bash

for i in 1 2 3
do
  for j in A B C
  do
    echo "Par: $i$j"
  done
done

Salida:

Par: 1A
Par: 1B
Par: 1C
Par: 2A
Par: 2B
Par: 2C
Par: 3A
Par: 3B
Par: 3C

7. Ejemplo avanzado: Usando for con condiciones

Supongamos que queremos encontrar archivos creados recientemente en un directorio y realizar acciones sobre ellos:

#!/bin/bash

for file in $(find . -type f -mtime -7)
do
  echo "Archivo modificado en la última semana: $file"
done

En este ejemplo, el comando find genera una lista de archivos, y el bucle for los recorre.


8. Alternativa moderna: for ((exp1; exp2; exp3))

Esta variante del bucle for es similar a los bucles de C. Se utiliza para iterar sobre expresiones aritméticas.

Ejemplo:

#!/bin/bash

for ((i=1; i<=5; i++))
do
  echo "Iteración $i"
done

Salida:

Iteración 1
Iteración 2
Iteración 3
Iteración 4
Iteración 5
  • i=1: Inicialización.
  • i<=5: Condición para continuar el bucle.
  • i++: Incremento en cada iteración.

9. Comparación for vs while

El bucle for es ideal cuando conoces de antemano la lista de elementos o el rango de iteración. Si necesitas ejecutar un bucle hasta que se cumpla una condición dinámica, es mejor usar un while.


Resumen

El bucle for en Bash es extremadamente versátil y se puede usar para:

  • Recorrer listas de palabras, números o archivos.
  • Procesar salidas de comandos.
  • Trabajar con rangos numéricos.
  • Anidar bucles para estructuras más complejas.

Bucle while

Un bucle while en Shell Script es una forma de ejecutar un bloque de código repetidamente mientras se cumpla una condición. Es ideal para tareas que necesitan repetirse hasta que algo cambie.


El bucle while en Shell Script

En Bash, el bucle while ejecuta comandos repetidamente mientras una condición sea verdadera. Su estructura básica es:

while [ condición ]; do
    # Código a ejecutar
done
  1. [ condición ]: Evalúa si la condición es verdadera. Si lo es, ejecuta el bloque de código dentro del dodone.
  2. do y done: Delimitan el cuerpo del bucle.

Ejemplo básico: Contador

Este script imprime números del 1 al 5:

#!/bin/bash

contador=1

while [ $contador -le 5 ]; do
    echo "Número: $contador"
    contador=$((contador + 1)) # Incrementa el contador
done

Explicación:

  • contador=1: Inicializamos la variable contador con 1.
  • [ $contador -le 5 ]: Comprueba si contador es menor o igual a 5.
  • echo: Imprime el valor actual de contador.
  • contador=$((contador + 1)): Incrementa la variable en 1.

Ejemplo avanzado: Leer un archivo línea por línea

Este script lee un archivo llamado nombres.txt y muestra cada línea en pantalla:

#!/bin/bash

archivo="nombres.txt"

while IFS= read -r linea; do
    echo "Nombre: $linea"
done < "$archivo"

Explicación:

  • IFS= read -r linea: Lee cada línea del archivo y la asigna a la variable linea.
  • done < "$archivo": Redirige el contenido de nombres.txt al bucle.

Ejemplo con un comando externo

Usa un bucle while para realizar una tarea en intervalos regulares, como comprobar si un servicio está activo:

#!/bin/bash

servicio="apache2"

while systemctl is-active --quiet $servicio; do
    echo "El servicio $servicio está activo"
    sleep 5 # Espera 5 segundos antes de comprobar nuevamente
done

echo "El servicio $servicio se detuvo."

Explicación:

  • systemctl is-active --quiet $servicio: Verifica si el servicio está activo.
  • sleep 5: Pausa el script durante 5 segundos antes de repetir el bucle.

Cuándo usar while

  • Repetir tareas hasta que se cumpla una condición.
  • Leer datos dinámicamente, como archivos o entradas del usuario.
  • Monitorizar eventos o servicios.

Break and continue

Vamos a explicar los comandos break y continue en Bash.


¿Qué hace break?

El comando break termina un bucle inmediatamente, sin importar si la condición del bucle todavía es verdadera. En tu ejemplo, se usa para salir del bucle while cuando el usuario selecciona la opción 4.


¿Qué hace continue?

El comando continue detiene la ejecución del ciclo actual y pasa directamente a la siguiente iteración del bucle. Esto significa que salta el resto del código en el ciclo actual, pero no rompe el bucle por completo.


Tu ejemplo adaptado con explicaciones

Aquí tienes un ejemplo funcional en un archivo .sh, incorporando break y continue:

#!/bin/bash

while true; do
    clear
    cat <<EOF

Por favor seleccione una opción:
1. opción a
2. opción b
3. opción c
4. salir

EOF

    read -p "Introduce la opción [1-4]: " REPLY

    if [ "$REPLY" -eq 4 ]; then
        echo "Saliendo del programa..."
        break # Termina el bucle si elige 4
    fi

    if ! [[ "$REPLY" =~ ^[1-3]$ ]]; then
        echo "Opción no válida, intenta nuevamente."
        sleep 2
        continue # Vuelve al inicio del bucle si la opción no es válida
    fi

    case $REPLY in
        1)
            echo "Has seleccionado la opción a."
            ;;
        2)
            echo "Has seleccionado la opción b."
            ;;
        3)
            echo "Has seleccionado la opción c."
            ;;
    esac

    echo "Presiona enter para continuar..."
    read # Pausa para que el usuario vea el resultado
done

Al final de la linea read -p "Introduce la opción [1-4]: " REPLY Vemos REPLY esta es la variable por defecto podriamos omitirla, Pero por claridad la dejamos. Si quisieramos usar otro nombre de varibale ahi si tenemos que ponerla. read -p “Introduce la opción [1-4]: ” opciones


Cómo funciona este ejemplo:

  1. while true:

    • El bucle se ejecuta indefinidamente hasta que se use break.
  2. break:

    • Si el usuario introduce 4, el bucle termina.
  3. continue:

    • Si el usuario introduce algo que no está entre 1 y 3, muestra un mensaje de error y salta directamente a la siguiente iteración del bucle.
  4. case:

    • Se utiliza para manejar las opciones seleccionadas por el usuario (1, 2, 3).

Guardar este script como archivo .sh

  1. Copia el código y guárdalo en un archivo llamado menu.sh:

    nano menu.sh
    
  2. Haz que el archivo sea ejecutable:

    chmod +x menu.sh
    
  3. Ejecuta el script:

    ./menu.sh
    

Con este ejemplo, el usuario puede navegar por el menú, y el script manejará correctamente las opciones, salidas y entradas no válidas.

La Sentencia until en Shell Script

La sentencia until en Bash es una estructura de control de bucles que ejecuta un bloque de código hasta que se cumpla una condición específica. Es la contraparte del bucle while, pero con la lógica invertida.


Sintaxis básica

until [ condición ]; do
    # Bloque de código a ejecutar
done
  1. condición: Es una expresión que se evalúa antes de cada iteración. El bucle continúa ejecutándose mientras la condición sea falsa.
  2. do y done: Delimitan el bloque de código que se ejecutará repetidamente.

¿Cómo funciona until?

El bucle until repite el bloque de código mientras la condición sea falsa. Una vez que la condición se evalúa como verdadera, el bucle termina.


Ejemplo básico

#!/bin/bash

contador=1

until [ $contador -gt 5 ]; do
    echo "Número: $contador"
    contador=$((contador + 1)) # Incrementa el contador
done

Explicación del ejemplo:

  1. Inicialización: contador=1.
  2. Condición: [ $contador -gt 5 ] (El bucle se ejecuta mientras $contador sea menor o igual a 5).
  3. Incremento: Se incrementa el valor de contador en cada iteración.

Salida esperada:

Número: 1
Número: 2
Número: 3
Número: 4
Número: 5

Ejemplo con entrada del usuario

Vamos a usar un bucle until para forzar al usuario a introducir una contraseña correcta:

#!/bin/bash

contraseña="secreta"
entrada=""

until [ "$entrada" = "$contraseña" ]; do
    read -sp "Introduce la contraseña: " entrada
    echo
    if [ "$entrada" != "$contraseña" ]; then
        echo "Contraseña incorrecta, intenta de nuevo."
    fi
done

echo "¡Contraseña correcta!"

Explicación del ejemplo:

  1. Condición: El bucle se ejecuta hasta que la entrada del usuario sea igual a "secreta".
  2. read -sp: Solicita al usuario introducir una contraseña sin mostrarla en la terminal.
  3. Mensaje: Si la contraseña es incorrecta, muestra un mensaje de error.

Diferencia entre while y until

whileuntil
Ejecuta el bloque mientras la condición es verdadera.Ejecuta el bloque mientras la condición es falsa.
Se detiene cuando la condición es falsa.Se detiene cuando la condición es verdadera.
Ejemplo: while [ $contador -le 5 ]; doEjemplo: until [ $contador -gt 5 ]; do

Ejemplo combinado: until con un contador decreciente

#!/bin/bash

contador=10

until [ $contador -lt 1 ]; do
    echo "Cuenta regresiva: $contador"
    contador=$((contador - 1))
done

echo "¡Despegue!"

Salida esperada:

Cuenta regresiva: 10
Cuenta regresiva: 9
Cuenta regresiva: 8
...
Cuenta regresiva: 1
¡Despegue!

¿Cuándo usar until?

  • Cuando necesitas ejecutar un bloque de código hasta que una condición sea verdadera.
  • Es útil en situaciones donde quieres forzar un comportamiento repetido hasta que el programa llegue a un estado deseado (como en validaciones o esperas condicionales).

Case

El Uso de case en Shell Script

La sentencia case en Bash se utiliza para comparar una variable o expresión contra múltiples patrones. Es similar al switch en otros lenguajes, pero más potente gracias al soporte para expresiones regulares y shell expansion.


Sintaxis básica

case variable in
    patrón1)
        # Bloque de código si coincide patrón1
        ;;
    patrón2)
        # Bloque de código si coincide patrón2
        ;;
    *)
        # Bloque de código si no coincide ningún patrón (opcional)
        ;;
esac
  1. variable: Es la entrada que se evalúa (por ejemplo, texto ingresado por el usuario).
  2. patrón: Son condiciones o expresiones que se comparan con el valor de la variable.
  3. ;;: Marca el final de cada bloque de código.
  4. *): Es el caso predeterminado (similar al default).

Ejemplo básico

#!/bin/bash

echo "Introduce una opción (a, b o c):"
read opcion

case $opcion in
    a)
        echo "Elegiste la opción A."
        ;;
    b)
        echo "Elegiste la opción B."
        ;;
    c)
        echo "Elegiste la opción C."
        ;;
    *)
        echo "Opción no válida."
        ;;
esac

Cómo funciona:

  1. Si el usuario introduce a, se ejecuta el bloque asociado al patrón a).
  2. Si introduce algo diferente a a, b o c, el caso *) se ejecuta como opción predeterminada.

Opciones avanzadas de case

1. Uso de comodines en los patrones

  • *: Coincide con cualquier cadena de caracteres.
  • ?: Coincide con un solo carácter.
  • [ ]: Define rangos o conjuntos de caracteres.
case $opcion in
    [a-c]) # Coincide con a, b o c
        echo "Seleccionaste una opción válida: $opcion"
        ;;
    ?)
        echo "Un solo carácter, pero no válido."
        ;;
    *)
        echo "Opción inválida."
        ;;
esac

2. Uso de Shell Expansion

Shell expansion permite trabajar con múltiples opciones de forma compacta, por ejemplo:

case $opcion in
    {start,stop,restart})
        echo "Ejecutando acción: $opcion"
        ;;
    *)
        echo "Acción desconocida."
        ;;
esac

Nota: Aunque las llaves {} funcionan en Bash para la expansión, es más común utilizar | para opciones múltiples.


Expresiones regulares en case

Aunque case no soporta expresiones regulares completas (como con grep o [[ ]]), sí permite usar patrones avanzados como:

  1. Inicio y fin de cadenas:

    • abc*: Coincide con cualquier cadena que comience con “abc”.
    • *xyz: Coincide con cualquier cadena que termine con “xyz”.
  2. Rangos de caracteres:

    • [a-z]: Coincide con cualquier carácter en el rango a a z.
    • [0-9]: Coincide con cualquier número.
  3. Patrones múltiples (usando |):

    • (patrón1|patrón2): Coincide con cualquiera de los patrones.

Ejemplo:

echo "Introduce un texto:"
read texto

case $texto in
    [0-9]*) # Comienza con un número
        echo "Comienza con un número."
        ;;
    *[a-z]) # Termina con una letra minúscula
        echo "Termina con una letra minúscula."
        ;;
    *.txt) # Coincide con archivos .txt
        echo "Es un archivo de texto."
        ;;
    *)
        echo "No coincide con ningún patrón."
        ;;
esac

Complemento con Shell Expansion

Shell expansion puede hacer más dinámico el uso de case, por ejemplo:

Usando case para comprobar archivos

archivo="prueba.txt"

case $archivo in
    *.txt)
        echo "Es un archivo de texto."
        ;;
    *.sh)
        echo "Es un script de shell."
        ;;
    *)
        echo "Tipo de archivo desconocido."
        ;;
esac

Listar comandos válidos

comando="start"

case $comando in
    start|stop|restart)
        echo "Ejecutando comando: $comando"
        ;;
    *)
        echo "Comando no reconocido."
        ;;
esac

Ejemplo práctico: Validación de entradas

#!/bin/bash

echo "Introduce tu opción (número del 1 al 5, o texto específico):"
read entrada

case $entrada in
    [1-5])
        echo "Ingresaste un número válido: $entrada"
        ;;
    start|stop|restart)
        echo "Comando reconocido: $entrada"
        ;;
    *.log)
        echo "Es un archivo de log: $entrada"
        ;;
    "")
        echo "No ingresaste nada."
        ;;
    *)
        echo "Entrada no válida."
        ;;
esac

Resumen

  • case es ideal para evaluar múltiples condiciones sin anidar varios if-else.
  • Soporta comodines, shell expansion y patrones avanzados.
  • Simplifica la lógica del script, especialmente cuando se trata de manejar múltiples opciones.

Uso de ;; y ;;&

Diferencias entre 1 | 2) y 2) en case

Cuando usas case en un script de Bash, hay dos maneras de manejar patrones que pueden coincidir con una misma entrada:

  1. 1 | 2): Permite que varias condiciones ejecuten el mismo bloque de código.
  2. 2): Define un caso exclusivo para una sola condición.

Ejemplo: Uso de 1 | 2) y 2)

Supongamos que tienes el siguiente script:

#!/bin/bash

read -p "Introduce un número: " num

case $num in
    1 | 2)
        echo "Seleccionaste 1 o 2."
        ;;
    2)
        echo "Seleccionaste exactamente 2."
        ;;
    *)
        echo "No coincide con ningún caso."
        ;;
esac
  1. Si introduces 1, el bloque 1 | 2) se ejecutará y mostrará:

    Seleccionaste 1 o 2.
  2. Si introduces 2, el primer bloque (1 | 2)) coincide primero, por lo que no se evalúa el segundo caso. La salida será:

    Seleccionaste 1 o 2.
  3. Si introduces cualquier otro número, el bloque predeterminado (*) se ejecutará:

    No coincide con ningún caso.

Nota: En case, una vez que encuentra un patrón coincidente, el bloque correspondiente se ejecuta y no evalúa el resto de los casos, a menos que uses ;& o ;;&.


Uso de ;&

El modificador ;& permite que, después de ejecutar un bloque de código, el flujo del programa continúe evaluando el siguiente caso sin importar la coincidencia.

Ejemplo con ;&

#!/bin/bash

read -p "Introduce un número: " num

case $num in
    1)
        echo "Es 1."
        ;;&
    2)
        echo "Es 2 (o después de 1 si usaste ;;&)."
        ;;
    *)
        echo "Es otra opción."
        ;;
esac
  1. Si introduces 1:

    • Coincide con el primer caso 1), imprime:
      Es 1.
    • Luego, continúa evaluando el caso 2) y, si es aplicable, ejecuta ese bloque también:
      Es 2 (o después de 1 si usaste ;;&amp;).

    Salida completa:

    Es 1.
    Es 2 (o después de 1 si usaste ;;&amp;).
  2. Si introduces 2, solo se ejecuta el bloque correspondiente:

    Es 2 (o después de 1 si usaste ;;&amp;).
  3. Si introduces cualquier otro número:

    • El caso predeterminado (*) se ejecuta:
      Es otra opción.

Uso de ;;&

El modificador ;;& permite que, después de ejecutar un bloque, el flujo del programa continúe evaluando todos los casos restantes para buscar coincidencias adicionales.

Ejemplo con ;;&

#!/bin/bash

read -p "Introduce un número: " num

case $num in
    1)
        echo "Es 1."
        ;;&
    2)
        echo "Es 2."
        ;;&
    *)
        echo "Es otra opción."
        ;;
esac
  1. Si introduces 1, el flujo pasa por cada caso y evalúa si también coincide con otros patrones:

    • Coincide con el primer caso y ejecuta:
      Es 1.
    • Luego, verifica si coincide con 2) y lo ejecuta:
      Es 2.
    • Finalmente, ejecuta el caso * porque siempre aplica:
      Es otra opción.

    Salida completa:

    Es 1.
    Es 2.
    Es otra opción.
  2. Si introduces 2, el flujo evaluará los siguientes casos:

    • Coincide con el caso 2) y ejecuta:
      Es 2.
    • Luego, evalúa el caso * y ejecuta:
      Es otra opción.

    Salida completa:

    Es 2.
    Es otra opción.
  3. Si introduces cualquier otro número:

    • Solo se ejecuta el caso predeterminado:
      Es otra opción.

Diferencias entre ;;, ;& y ;;&

ModificadorComportamiento
;;Termina el caso actual y no evalúa los siguientes casos.
;&Continúa con el siguiente caso, ejecutando su bloque de código, sin comprobar coincidencias.
;;&Continúa evaluando todos los casos restantes, verificando coincidencias.

Conclusión

  • 1 | 2) agrupa patrones para ejecutar el mismo bloque de código.
  • 2) evalúa exclusivamente el caso donde la variable es igual a 2.
  • ;& permite ejecutar el siguiente caso sin verificar coincidencias.
  • ;;& fuerza la evaluación de todos los casos restantes, buscando coincidencias adicionales.

Ejemplo de proyecto final

#!/bin/bash

# Este programa parsea los resultados de nmap y construye un documento HTML

TITULO="Resultados nmap"
FECHA_ACTUAL="$(date)"
TIMESTAMP="Informe generado el $FECHA_ACTUAL por el usuario $USERNAME"

nmap_report() {
    # Generamos el reporte raw con nmap
    echo "[INFO] Ejecutando nmap en la red $1, por favor espere unos segundos..."
    sudo nmap -sV "$1" > "$2"
    echo "[OK] Fichero $2 generado correctamente"

    # Dividimos el fichero por líneas vacías
    echo "[INFO] Dividiendo el fichero $2..."
    csplit "$2" '/^$/' {*} > /dev/null
    echo "[OK] Fichero $2 dividido en los siguientes ficheros: $(ls xx*)"
    return 0
}

result_parser() {
    for i in xx*; do
        host_ip=$(grep -E 'Nmap scan report ' $i | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
        if [ "$host_ip" ]; then
            echo "<tr>"
            echo "<td>$host_ip</td>"

            # Obtenemos los puertos abiertos
            puertos_abiertos=$(grep -E -h '^[0-9]{1,5}/(tcp|udp) open' $i | grep -E -o '^[0-9]{1,5}/(tcp|udp)')
            if [ "$puertos_abiertos" ]; then
                echo "<td>$puertos_abiertos</td>"
            else
                echo "<td>No hay puertos abiertos</td>"
            fi

            # Obtenemos los servicios
            servicios=$(grep -E -h '^[0-9]{1,5}/(tcp|udp) open' $i | grep -E -o '  .*  ')
            if [ "$servicios" ]; then
                echo "<td>$servicios</td>"
            else
                echo "<td>No hay servicios expuestos</td>"
            fi

            echo "</tr>"
        fi
    done
    return 0
}

generar_html() {
    cat <<EOF

<html>
    <head>
        <title>$TITULO</title>
    </head>
    <style>
    table {
        font-family: arial, sans-serif;
        border-collapse: collapse;
        width: 100%;
    }

    td, th {
        border: 1px solid #dddddd;
        text-align: left;
        padding: 8px;
    }

    tr:nth-child(even) {
        background-color: #dddddd;
    }
    </style>
    <body>
        <h1>$TITULO</h1>
        <p>$TIMESTAMP</p>
        <table>
            <tr>
                <th>Host IP</th>
                <th>Puertos abiertos</th>
                <th>Servicio</th>
            </tr>
            $(result_parser)
        </table>
    </body>
</html>

EOF
}

if [ $(find salida_nmap.raw -mmin -30 2>/dev/null) ]; then
    while true; do
        read -p "Existe salida_nmap.raw con antigüedad menor a 30 minutos. ¿Sobreescribir? [y/n]: " REPLY
        case "$REPLY" in
            y)
                # Generamos el reporte raw con nmap
                nmap_report "192.168.239.0/24" "salida_nmap.raw"
                break
                ;;
            n)
                echo "[INFO] Utilizando el fichero salida_nmap.raw existente"
                break
                ;;
            *)
                echo "[ERROR] Respuesta no válida. Por favor, introduzca 'y' o 'n'."
                ;;
        esac
    done
else
    # Generamos el reporte raw con nmap
    nmap_report "192.168.239.0/24" "salida_nmap.raw"
fi

# Generamos el reporte con los resultados de nmap en HTML
echo "[INFO] Generando reporte HTML..."
generar_html > resultados_nmap.html
echo "[OK] Reporte resultados_nmap.html generado correctamente"