Comparación de Condicionales en Bash: Cuándo Usar test, [ y [[
Compara test, corchetes simples y corchetes dobles para que tus condicionales en Bash sean portátiles, seguros y legibles.
Comparación de Condicionales en Bash: Cuándo Usar test, [ y [[
Cuando un condicional en Bash se comporta de manera extraña, el problema suele ser la construcción que elegiste. test, [ ] y [[ ]] se ven similares, pero manejan las comillas, los patrones, las expresiones regulares y la portabilidad de manera diferente.
Esta guía compara las tres formas para que puedas escribir condicionales que sean seguros, legibles y apropiados para el shell en el que realmente se ejecuta tu script.
El Comando test: La Base
El comando test es una de las formas más antiguas y fundamentales de evaluar condiciones en scripts de shell. Es un comando incorporado en la mayoría de los shells modernos y forma parte del estándar POSIX, lo que lo hace altamente portátil. test evalúa una expresión y devuelve un estado de salida de 0 (verdadero) o 1 (falso).
Uso Básico
El comando test toma uno o más argumentos, que forman la expresión a evaluar. Verifica atributos de archivos, comparaciones de cadenas y comparaciones de enteros.
# Verificar si un archivo existe
if test -f "myfile.txt"; then
echo "myfile.txt existe y es un archivo regular."
fi
# Verificar si dos cadenas son iguales
NOMBRE="Alice"
if test "$NOMBRE" = "Alice"; then
echo "El nombre es Alice."
fi
# Verificar si un número es mayor que otro
CONTEO=10
if test "$CONTEO" -gt 5; then
echo "El conteo es mayor que 5."
fi
Operadores Comunes de test
- Operadores de Archivo:
-f(archivo regular),-d(directorio),-e(existe),-s(no vacío),-r(legible),-w(escribible),-x(ejecutable). - Operadores de Cadena:
=(igual),!=(diferente),-z(cadena vacía),-n(cadena no vacía). - Operadores de Entero:
-eq(igual),-ne(diferente),-gt(mayor que),-ge(mayor o igual que),-lt(menor que),-le(menor o igual que).
Consejo: Siempre pon entre comillas las variables usadas con test (por ejemplo, "$NOMBRE") para evitar problemas con la división de palabras y la expansión de nombres de ruta si el valor de la variable contiene espacios o caracteres glob.
Corchetes Simples [ ]: La Forma de test
La construcción de corchetes simples [ ] es una sintaxis alternativa para el comando test. En muchos shells, [ es un comando incorporado del shell, y los sistemas a menudo también proporcionan un /usr/bin/[ externo. La diferencia clave es que [ requiere un ] de cierre como su último argumento. Al igual que test, cumple con POSIX.
Sintaxis y Semántica
# Equivalente a test -f "myfile.txt"
if [ -f "myfile.txt" ]; then
echo "myfile.txt existe y es un archivo regular usando [ ]."
fi
# Equivalente a test "$NOMBRE" = "Alice"
NOMBRE="Bob"
if [ "$NOMBRE" != "Alice" ]; then
echo "El nombre no es Alice."
fi
Observa el espacio obligatorio después de [ y antes de ]. Estos se tratan como argumentos separados para el comando [.
Poner Variables entre Comillas: Un Detalle Crítico
Debido a que [ ] es fundamentalmente el comando test, hereda los mismos comportamientos con respecto a la división de palabras y la expansión de nombres de ruta. Esto significa que las variables sin comillas pueden provocar un comportamiento inesperado o vulnerabilidades de seguridad.
Considera este ejemplo:
#!/bin/bash
ENTRADA="archivo con espacios.txt"
# PELIGROSO: La variable sin comillas causará problemas si ENTRADA contiene espacios
# El shell realizará la división de palabras, tratando "archivo" y "con espacios.txt" como argumentos separados
# lo que lleva a un error de sintaxis o una evaluación incorrecta.
# if [ -f $ENTRADA ]; then echo "Encontrado"; else echo "No encontrado"; fi
# CORRECTO: Pon la variable entre comillas para tratarla como un solo argumento
if [ -f "$ENTRADA" ]; then
echo "'archivo con espacios.txt' existe."
else
echo "'archivo con espacios.txt' no existe o no es un archivo regular."
fi
Sin comillas, $ENTRADA se expandiría a archivo con espacios.txt, y [ -f archivo con espacios.txt ] sería interpretado como un error de sintaxis por el comando [ porque -f espera solo un operando. Poner entre comillas asegura que $ENTRADA se pase como un solo argumento, "archivo con espacios.txt".
Peligros de la División de Palabras y la Expansión de Nombres de Ruta
Tanto test como [ están sujetos a los comportamientos predeterminados del shell de división de palabras y expansión de nombres de ruta (globbing). Si una variable contiene espacios o caracteres glob (*, ?, [ ]) y no está entre comillas, el shell la expandirá antes de que test o [ vean los argumentos. Esto puede provocar errores de sintaxis o comparaciones incorrectas cuando los caracteres glob coinciden con archivos existentes.
Corchetes Dobles [[ ]]: La Palabra Clave Moderna de Bash
La construcción de corchetes dobles [[ ]] es una palabra clave de Bash (también compatible con Ksh y Zsh), no un comando externo ni un alias. Esta distinción es crucial, ya que permite que [[ ]] se comporte de manera diferente y ofrezca funcionalidad mejorada y mayor seguridad en comparación con test o [ ].
Funcionalidad Mejorada
[[ ]] introduce varias características potentes que no están disponibles con test o [:
Sin División de Palabras ni Expansión de Nombres de Ruta: Las variables dentro de
[[ ]]generalmente no necesitan estar entre comillas (aunque a menudo es una buena práctica hacerlo para mayor claridad). El shell maneja el contenido de[[ ]]como una sola unidad, evitando la división de palabras y la expansión de nombres de ruta. Esto reduce significativamente los errores comunes de scripting y los riesgos de seguridad.# No es necesario poner variables entre comillas (aunque sigue siendo seguro hacerlo) ENTRADA="archivo con espacios.txt" if [[ -f $ENTRADA ]]; then # $ENTRADA se trata como una sola cadena aquí echo "'$ENTRADA' existe." fiGlobbing para Comparación de Cadenas: Los operadores
==y!=realizan coincidencia de patrones (globbing) en lugar de igualdad estricta de cadenas cuando se usan dentro de[[ ]]. Esto significa que puedes usar*,?y[]como comodines.NOMBRE_ARCHIVO="mi_documento.txt" if [[ "$NOMBRE_ARCHIVO" == *".txt" ]]; then # Verifica si NOMBRE_ARCHIVO termina en .txt echo "¡Es un archivo de texto!" fi # Nota: Para igualdad estricta de cadenas sin globbing, usa `test` o `[ ]` con `=` # o asegúrate de que no haya caracteres glob presentes en el lado derecho de `==` en [[ ]] # (o pon entre comillas el lado derecho si contiene caracteres glob literales que deseas coincidir literalmente).Coincidencia de Expresiones Regulares: El operador
=~te permite realizar coincidencias de expresiones regulares.
DIRECCION_IP="192.168.1.100"
if [[ "$DIRECCION_IP" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
echo "Formato de IP válido."
fi
# Importante: El patrón de expresión regular en el lado derecho de =~ generalmente NO debe estar entre comillas
# si contiene caracteres que de otro modo se tratarían como patrones glob.
# Si la expresión regular está en una variable, también debe estar sin comillas.
# Ejemplo de patrón: ^[A-Za-z]+$
```
4. **Operadores Lógicos `&&` y `||`**: `[[ ]]` admite los operadores lógicos de estilo C `&&` (Y) y `||` (O) más intuitivos para combinar múltiples condiciones, junto con `!` para la negación. Estos operadores tienen una evaluación de cortocircuito y precedencia adecuadas, a diferencia de `-a` y `-o` de `test`.
```bash
EDAD=25
if [[ "$NOMBRE" == "Alice" && "$EDAD" -ge 18 ]]; then
echo "Alice es adulta."
fi
if [[ "$USUARIO" == "root" || -w /etc/fstab ]]; then
echo "O es root o puede escribir en fstab."
fi
```
### Naturaleza Específica de Bash
Si bien `[[ ]]` ofrece ventajas significativas, su principal inconveniente es que es una extensión de Bash/Ksh/Zsh y no forma parte del estándar POSIX. Esto significa que los scripts que dependen de `[[ ]]` pueden no ser portátiles a `sh`, `dash` o sistemas Unix-like antiguos/mínimos.
## Comparación Lado a Lado: `test` vs. `[` vs. `[[`
Aquí hay una tabla que resume las diferencias clave:
| Característica | `test` | `[ ]` | `[[ ]]` |
| :------------------------- | :------------------------------- | :---------------------------------- | :---------------------------------------- |
| **Tipo** | Comando incorporado (o externo) | Comando incorporado (alias de `test`) | Palabra clave del shell (Bash, Ksh, Zsh) |
| **Cumple con POSIX** | Sí | Sí | No |
| **Requiere `]` de Cierre** | No | Sí (como último argumento) | Sí (como parte de la palabra clave) |
| **División de Palabras** | Sí, en variables sin comillas | Sí, en variables sin comillas | No, las variables se tratan como cadenas únicas |
| **Expansión de Nombres de Ruta** | Sí, en variables sin comillas | Sí, en variables sin comillas | No |
| **Coincidencia de Patrones Glob** | No para igualdad de cadenas | No para igualdad de cadenas | Sí con el lado derecho sin comillas de `==` o `!=` |
| **Expresiones Regulares** | No | No | Sí con `=~` |
| **Y/O Lógicos** | `-a`, `-o` existen pero son fáciles de malinterpretar | `-a`, `-o` existen pero son fáciles de malinterpretar | `&&`, `||` con comportamiento de cortocircuito normal |
| **Comandos Compuestos** | Requiere llamadas `test` separadas | Requiere llamadas `[` separadas | Puede combinar expresiones directamente (`&&`/`||`)|
| **Poner Variables entre Comillas** | **Obligatorio** por seguridad | **Obligatorio** por seguridad | Generalmente no es necesario, pero es una buena práctica |
## Cuándo Usar Cada Uno
Elegir la construcción condicional correcta depende principalmente de tus requisitos de portabilidad y la complejidad de tu lógica condicional.
### Cumplimiento de POSIX vs. Características Modernas de Bash
- **Usa `test` o `[ ]` cuando...**
- **La portabilidad es primordial**: Si tu script necesita ejecutarse en cualquier shell compatible con POSIX (`sh`, `dash`, sistemas antiguos, etc.), `test` o `[ ]` son tus únicas opciones confiables.
- Tus condiciones son simples (verificaciones de archivos, comparaciones básicas de cadenas/enteros).
- Te sientes cómodo poniendo entre comillas cuidadosamente todas las variables y usando `&&`/`||` a nivel de shell fuera de los corchetes cuando necesitas lógica compuesta.
- **Usa `[[ ]]` cuando...**
- **Estás escribiendo exclusivamente para Bash** (o Ksh/Zsh) y no necesitas portabilidad POSIX.
- Necesitas funciones avanzadas como coincidencia de patrones glob, coincidencia de expresiones regulares u operadores lógicos `&&`/`||` de estilo C.
- Deseas las funciones de seguridad mejoradas que evitan la división de palabras y la expansión de nombres de ruta, lo que lleva a un código más robusto y menos propenso a errores.
- Tus condiciones implican una lógica compleja que sería engorrosa con `test -a`/`-o`.
### Mejores Prácticas y Recomendaciones
1. **Prioriza `[[ ]]` para Scripts de Bash**: Si tu script está destinado a Bash, `[[ ]]` es generalmente la opción preferida debido a su mayor seguridad, funcionalidad extendida y sintaxis más intuitiva para condiciones complejas. Reduce drásticamente los errores comunes de scripting relacionados con las comillas y los caracteres especiales.
2. **Siempre Pon entre Comillas en `test` y `[ ]`**: Si *debes* usar `test` o `[ ]` para cumplir con POSIX, adquiere el hábito de **poner siempre entre comillas tus variables** para evitar comportamientos inesperados debido a la división de palabras y la expansión de nombres de ruta.
```bash
# Buena práctica para [ ] y test
VAR="una cadena con espacios"
if [ -n "$VAR" ]; then echo "No vacío"; fi
```
3. **Ten Cuidado con la Coincidencia de Patrones**: En `test` y `[ ]`, `=` se usa para la igualdad de cadenas. En `[[ ]]`, `=` y `==` pueden realizar coincidencias de patrones cuando el lado derecho no está entre comillas. Pon entre comillas el lado derecho cuando desees una comparación de cadenas literal.
4. **Expresiones Regulares con `=~`**: Al usar `=~` en `[[ ]]`, el lado derecho generalmente no debe estar entre comillas para permitir que el shell lo interprete como un patrón de expresión regular, no como una cadena literal para coincidir.
```bash
# El patrón de expresión regular sin comillas es correcto para =~ en [[ ]]
if [[ "$LINEA" =~ ^Error: ]]; then echo "Error encontrado"; fi
```
## Conclusión
Usa `[ ]` o `test` cuando tu script deba ejecutarse bajo POSIX `sh`. Usa `[[ ]]` cuando tu shebang sea Bash y desees un manejo de variables más seguro, coincidencia glob, coincidencia de expresiones regulares y condiciones compuestas más limpias. El hábito principal es simple: adapta la sintaxis condicional al shell y pon entre comillas deliberadamente.