Los scripts de shell a menudo requieren la capacidad de tomar decisiones basadas en condiciones. Bash ofrece varias construcciones para evaluar expresiones, siendo las más comunes el comando test y sus alias [ ], así como la expresión extendida [[ ]]. Comprender las diferencias y capacidades de cada una es fundamental para escribir scripts robustos y eficientes.
Evaluación de Archivos y Directorios
Bash proporciona operadores para verificar la existencia y el tipo de archivos y directorios en el sistema de archivos:
-f <ruta>: Verdadero si la ruta especificada es un archivo regular existente.-d <ruta>: Verdadero si la ruta especificada es un directorio existente.-e <ruta>: Verdadero si la ruta existe (sea archivo, directorio, enlace simbólico, etc.).-b <ruta>: Verdadero si la ruta es un dispositivo de bloque.-c <ruta>: Verdadero si la ruta es un dispositivo de caracteres.-S <ruta>: Verdadero si la ruta es un socket.-L <ruta>: Verdadero si la ruta es un enlace simbólico.-p <ruta>: Verdadero si la ruta es una tubería (FIFO).
Atributos de Archivos
También es posible verificar los permisos y otras propiedades de un archivo:
-r <archivo>: Verdadero si el archivo es legible por el usuario actual.-w <archivo>: Verdadero si el archivo es escribible por el usuario actual.-x <archivo>: Verdadero si el archivo es ejecutable por el usuario actual.-s <archivo>: Verdadero si el archivo existe y su tamaño es mayor que cero (no vacío).-u <archivo>: Verdadero si el archivo tiene el bit SUID activado.-g <archivo>: Verdadero si el archivo tiene el bit SGID activado.-k <archivo>: Verdadero si el archivo tiene el bit pegajoso (sticky bit) activado.
Comparación entre Archivos
Podemos comparar la antigüedad o la identidad de dos archivos:
<archivo1> -nt <archivo2>: Verdadero si<archivo1>es más reciente que<archivo2>.<archivo1> -ot <archivo2>: Verdadero si<archivo1>es más antiguo que<archivo2>.<archivo1> -ef <archivo2>: Verdadero si<archivo1>y<archivo2>son el mismo archivo (ej. enlaces duros).
# Ejemplo de comparación de archivos
touch archivo_viejo.txt
sleep 1
touch archivo_nuevo.txt
if [ "archivo_nuevo.txt" -nt "archivo_viejo.txt" ]; then
echo "archivo_nuevo.txt es más reciente."
fi
# Limpieza
rm archivo_viejo.txt archivo_nuevo.txt
Operadores de Comparación y Lógicos
Las expresiones condicionales utilizan diversos operadores para comparar valores y combinar condiciones:
Comparación de Cadenas y Números Enteros:
=(o==para[[ ]]): Igual a. Se usa para cadenas en[ ]y cadenas/patrones en[[ ]].!=: Diferente de. Se usa para cadenas en[ ]y cadenas/patrones en[[ ]].<: Menor que (lexicográfico para cadenas en[[ ]]; requiere escape como\<para cadenas en[ ]).>: Mayor que (lexicográfico para cadenas en[[ ]]; requiere escape como\>para cadenas en[ ]).-eq: Igual a (numérico).-ne: Diferente de (numérico).-lt: Menor que (numérico).-gt: Mayor que (numérico).-le: Menor o igual que (numérico).-ge: Mayor o igual que (numérico).-z <cadena>: Verdadero si la cadena está vacía (longitud cero).-n <cadena>: Verdadero si la cadena no está vacía (longitud mayor que cero).
Operadores Lógicos Booleanos:
-a: AND lógico. Usado entesty[ ].-o: OR lógico. Usado entesty[ ].&&: AND lógico. Usado en[[ ]]y como operador de control en la línea de comandos.||: OR lógico. Usado en[[ ]]y como operador de control en la línea de comandos.
El Comando test
El comando test evalúa una expresión y devuelve un estado de salida de 0 (verdadero) o 1 (falso). Es la base de todas las demás construcciones condicionales en scripts de shell.
# Ejemplos con el comando test
test -f /etc/passwd && echo "El archivo /etc/passwd existe."
test 5 -eq 5 && echo "5 es igual a 5."
valor=10
if test $valor -gt 0; then
echo "El valor es positivo."
fi
Es crucial separar los operadores y los operandos con espacios. Por ejemplo, test 1=1 es incorrecto, debe ser test 1 = 1.
Expresiones Condicionales con Corchetes Simples \[ \]
La sintaxis [ expresión ] es un alias para el comando test. Hereda todas sus reglas y limtiaciones. Al igual que con test, cada elemento (operador, operando) debe estar separado por un espacio.
# Ejemplos con [ ]
[ 10 -eq 10 ] && echo "Igualdad numérica confirmada."
# Comparación de cadenas con operadores relacionales (<, >) requiere escape
# De lo contrario, Bash interpreta < y > como operadores de redirección de E/S.
cadena1="manzana"
cadena2="pera"
[ "$cadena1" \< "$cadena2" ] && echo "'manzana' es menor que 'pera' (lexicográficamente)."
# Uso de operadores lógicos -a (AND) y -o (OR)
numero=7
[ $numero -gt 5 -a $numero -lt 10 ] && echo "$numero está entre 5 y 10."
# Errores comunes:
# [ 2 < 1 ] && echo "Esto fallará por redirección de E/S"
# [ 1 -eq 1 && 2 -eq 2 ] && echo "Esto es sintaxis incorrecta para [ ]"
Dentro de [ ], los operadores <, >, && y || no se comportan como operadores de comparación o lógicos. < y > se interpretan como redirecciones de entrada/salida, y &&/|| como operadores de control de flujo. Para comparaciones de cadenas, < y > deben ser escapados (\<, \>), y para lógica booleana se usan -a y -o.
Expresiones Condicionales con Doble Corchete \[\[ \]\]
La construcción [[ expresión ]] es una característica extendida de Bash que ofrece mayor flexibilidad y potencia. Es un "comando" compuesto, no un comando regular, lo que significa que no se somete a la expansión de palabras y tiene un comportamiento especial.
# Ejemplos con [[ ]]
[[ 20 -eq 20 ]] && echo "Igualdad numérica extendida."
# < y > funcionan directamente para comparación lexicográfica (cadenas) o numérica (si son números)
[[ "abc" < "xyz" ]] && echo "'abc' es menor que 'xyz'."
[[ 15 < 25 ]] && echo "15 es menor que 25 (numéricamente)."
# Soporte directo para && (AND) y || (OR) lógicos
edad=30
[[ $edad -gt 18 && $edad -lt 65 ]] && echo "Edad en rango laboral."
# Importante: [[ ]] no utiliza -a ni -o para la lógica booleana
# [[ $edad -gt 18 -a $edad -lt 65 ]] # Esto generaría un error de sintaxis en [[ ]]
Diferencias Clave entre \[\[ \]\] y \[ \]
-
Operadores Lógicos:
[ ]requiere-apara AND y-opara OR.[[ ]]utiliza&¶ AND y||para OR, similar a la sintaxis C/Java.# Comprobación de existencia de un archivo Y si es ejecutable archivo="/bin/ls" # Usando [ ] [ -f "$archivo" -a -x "$archivo" ] && echo "El archivo existe y es ejecutable (con [ ])." # Usando [[ ]] [[ -f "$archivo" && -x "$archivo" ]] && echo "El archivo existe y es ejecutable (con [[ ]])." -
Redirección vs. Comparación:
En
[ ], los operadores<y>deben ser escapados (\<,\>) para realizar comparaciones de cadenas, ya que de otro modo Bash los interpreta como operadores de redirección de E/S.En
[[ ]],<y>no necesitan ser escapados y realizan comparaciones lexicográficas de cadenas (o numéricas si los operandos son números).# Comparación de cadenas "beta" con "alpha" val_beta="beta" val_alpha="alpha" # Usando [ ] con escape [ "$val_beta" \> "$val_alpha" ] && echo "'beta' es mayor que 'alpha' (con [ ] y escape)." # Usando [[ ]] sin escape [[ "$val_beta" > "$val_alpha" ]] && echo "'beta' es mayor que 'alpha' (con [[ ]] sin escape)." -
Expansión Aritmética:
[ ]no realiza evaluación aritmética directa. Para ello, se necesita una expansión aritmética explícita ($((...))).[[ ]]realiza la expansión aritmética dentro de sus expresiones.# Evaluar si 10 + 5 es igual a 15 resultado_suma="10+5" # Usando [ ] - requiere expansión explícita [ $((10+5)) -eq 15 ] && echo "Suma correcta con [ ] y expansión." # Usando [[ ]] - evaluación automática [[ 10+5 -eq 15 ]] && echo "Suma correcta con [[ ]] y evaluación automática." -
Coincidencia de Patrones (Regex):
[[ ]]permite la coincidencia de patrones (expresiones regulares extendidas) usando el operador=~, o patrones tipo glob (wildcards) con=o==.[ ]solo admite la coincidencia de cadenas literales o patrones tipo glob simples (*,?) si la shell expande los wildcards antes de que[ ]los reciba, lo cual puede ser inconsistente.# Verificar si un nombre de archivo termina en .log nombre_archivo="mi_log_del_dia.log" # Usando [[ ]] para coincidencia de patrón glob [[ "$nombre_archivo" == *.log ]] && echo "El archivo termina en .log (con [[ ]] glob)." # Usando [[ ]] para expresión regular [[ "$nombre_archivo" =~ \.log$ ]] && echo "El archivo termina en .log (con [[ ]] regex)." # [ ] no soporta coincidencias de patrones de esta manera directamente # [ "$nombre_archivo" == *.log ] # Esto no funcionará como se espera para coincidencia de patrón. # [ "$nombre_archivo" =~ \.log$ ] # Esto causará un error de sintaxis.