Introducción a los Scripts de Dash: Diferencias con Bash y Cómo Escribir para Compatibilidad
¿Alguna vez has escrito un script de shell que funciona en tu PC pero no en el servidor? Es posible que la causa sea la diferencia entre Dash y Bash.
En este artículo, explicaremos la sintaxis básica de Dash, el shell real detrás de /bin/sh en muchos sistemas Linux (especialmente Debian y Ubuntu). Hemos preparado abundantes ejemplos de código para que incluso los principiantes puedan copiar, pegar y experimentar que "¡funciona!". También hemos resumido los puntos a tener en cuenta sobre la compatibilidad con Bash, para que puedas adquirir la habilidad de escribir scripts más robustos y portátiles.
Para empezar, ¿qué es Dash? ¿En qué se diferencia de Bash?
Lo que normalmente llamamos "script de shell" se refiere en la mayoría de los casos a Bash (Bourne Again SHell). Bash es muy potente y permite usar muchas notaciones convenientes.
Por otro lado, Dash (Debian Almquist SHell) es un shell más simple y ligero que cumple con el estándar POSIX. Debido a su alta velocidad de operación, se utiliza en sistemas operativos como Debian y Ubuntu para los scripts de inicio del sistema y como el shell estándar (/bin/sh).
En otras palabras, un script que comienza con #!/bin/sh podría funcionar en tu PC (entorno Bash) pero fallar con un error en un servidor (entorno Dash). Para evitar esto, el camino más corto es aprender a escribir en un formato compatible con POSIX que Dash también pueda interpretar.
Sintaxis Básica de Scripts de Dash [Puedes Copiar y Pegar]
Primero, familiaricémonos con la sintaxis básica de Dash. Todos los ejemplos de código comienzan con #!/bin/sh, por lo que puedes guardarlos en un archivo y ejecutarlos directamente.
1. ¡Hola, Mundo! - Imprimir una cadena de texto
El tradicional "Hola, Mundo". Usamos el comando echo para imprimir cadenas de texto. Esto es exactamente igual que en Bash.
#!/bin/sh
# Al principio del script, se escribe un 'shebang' para especificar con qué shell se debe ejecutar
echo "Hello, Dash World!"
2. Uso de variables
La regla al definir variables es no poner espacios a los lados del =. Para usarlas, se antepone un $ al nombre de la variable. Esto también es igual que en Bash.
#!/bin/sh
GREETING="Hola"
TARGET="Mundo"
echo "${GREETING}, ${TARGET}!" # Se recomienda encerrar los nombres de las variables entre {} para mayor claridad
3. Bifurcación condicional con 'if'
Para las bifurcaciones condicionales, usamos la sentencia if. La expresión condicional se encierra entre [ ... ]. Este es el primer punto de atención, ya que [[ ... ]], comúnmente visto en Bash, no se puede usar en Dash (lo explicaremos en detalle más adelante).
#!/bin/sh
USER_NAME="Alice"
# Es obligatorio dejar un espacio antes y después de los [ ]
if [ "$USER_NAME" = "Alice" ]; then
echo "Bienvenida, Alice."
else
echo "¿Quién eres tú?"
fi
4. Repetición con bucles 'for'
Escribamos un bucle for simple usando una lista separada por espacios.
#!/bin/sh
# Procesar una cadena de texto separada por espacios con un bucle
for fruit in apple banana cherry
do
echo "Me gusta ${fruit}."
done
[Lo más importante] Puntos a tener en cuenta para la compatibilidad con Bash
Aquí empieza lo importante. Funciones convenientes que se usan habitualmente en Bash pueden causar errores en Dash. Veamos algunos ejemplos representativos.
Diferencia 1: Arrays
Dash no soporta arrays. Esta es una diferencia muy grande.
❌ La forma de Bash (no funciona en Dash)
#!/bin/sh
# ¡Este código dará un error en Dash!
fruits=("apple" "banana" "cherry")
echo "La primera fruta es ${fruits[0]}."
⭕ Alternativa en Dash
Se puede lograr un procesamiento similar usando cadenas de texto separadas por espacios o la lista de argumentos ($@).
#!/bin/sh
# Procesamiento de bucle compatible con POSIX
fruit_list="apple banana cherry"
for fruit in $fruit_list
do
echo "Procesando fruta: $fruit"
done
Diferencia 2: Expresiones condicionales [[ ... ]] vs [ ... ]
Dash no puede interpretar [[ ... ]]. Para las expresiones condicionales, usemos siempre [ ... ] (que es una sintaxis abreviada para el comando test).
❌ La forma de Bash (no funciona en Dash)[[ es útil para la coincidencia de cadenas y operadores lógicos (&&, ||), pero es una extensión propia de Bash.
#!/bin/sh
# ¡Este código dará un error en Dash!
COUNT=10
if [[ "$USER" = "root" && $COUNT -gt 5 ]]; then
echo "Condición cumplida."
fi
⭕ La forma de Dash (compatible con POSIX)
Usando [ ... ], el Y lógico se representa con -a y el O lógico con -o.
#!/bin/sh
# Escritura consciente de la compatibilidad
COUNT=10
# Para combinar múltiples condiciones, se usa -a (AND) o -o (OR)
if [ "$USER" = "root" -a $COUNT -gt 5 ]; then
echo "Condición cumplida."
fi
Diferencia 3: Declaración de funciones con la palabra clave function
En Dash no se puede usar la palabra clave function. Las funciones se declaran con el formato nombre_funcion() { ... }.
❌ La forma de Bash (no funciona en Dash)
#!/bin/sh
# ¡Este código dará un error en Dash!
function say_hello() {
echo "Hello from function!"
}
say_hello
⭕ La forma de Dash (compatible con POSIX)
Esta es la forma estándar más antigua y, por supuesto, también funciona en Bash.
#!/bin/sh
# Declaración de función compatible con POSIX
say_hello() {
echo "¡Hola! Llamado desde una función."
}
# Llamada a la función
say_hello
Diferencia 4: Sustitución de procesos <()
La sustitución de procesos <(), que permite tratar la salida de un comando como un archivo, es una característica de Bash.
❌ La forma de Bash (no funciona en Dash)
#!/bin/sh
# ¡Este código dará un error en Dash!
# El comando comm compara dos archivos ordenados
comm <(ls -1 dir1) <(ls -1 dir2)
⭕ Alternativa en Dash
Guarda la salida en un archivo temporal o procésala hábilmente con una tubería (pipe).
#!/bin/sh
# Método usando archivos temporales
TMP1="/tmp/dir1_list"
TMP2="/tmp/dir2_list"
ls -1 dir1 > "$TMP1"
ls -1 dir2 > "$TMP2"
comm "$TMP1" "$TMP2"
# No olvides limpiar después
rm "$TMP1" "$TMP2"
Diferencia 5: Expansión de llaves {1..10}
La expansión de llaves, para generar números o cadenas de texto secuenciales, es una característica de Bash.
❌ La forma de Bash (no funciona en Dash)
#!/bin/sh
# En Dash, este código imprimirá literalmente la cadena de texto {1..5}
echo "Creando archivos archivo{1..5}.txt"
touch file_{1..5}.txt
⭕ Alternativa en Dash
Para generar secuencias numéricas, usemos el clásico comando seq.
#!/bin/sh
# Combinar el comando seq con un bucle for
echo "Creando archivos secuenciales del 1 al 5"
for i in $(seq 1 5)
do
touch "file_${i}.txt"
echo "Se ha creado el archivo número ${i}."
done
Ejemplo práctico: Escribir un script útil en Dash
Finalmente, aprovechemos lo aprendido para escribir un script un poco más práctico. Este script mueve todos los archivos .log del directorio actual a un directorio de copia de seguridad con la fecha del día. Por supuesto, es compatible con Dash.
#!/bin/sh
# Obtener la fecha de hoy en formato AAAA-MM-DD
# El formato del comando date está estandarizado en POSIX, por lo que es seguro
BACKUP_DIR="backup_$(date +%Y-%m-%d)"
# Si el directorio de copia de seguridad no existe, créalo
if [ ! -d "$BACKUP_DIR" ]; then
echo "Creando el directorio de copia de seguridad ${BACKUP_DIR}."
mkdir "$BACKUP_DIR"
fi
# Procesar los archivos .log con un bucle
# La expansión de comodines *.log es una función estándar del shell
for file in *.log
do
# Verificar si el archivo existe y es un archivo regular
if [ -f "$file" ]; then
echo "Moviendo ${file} a ${BACKUP_DIR}/."
mv "$file" "$BACKUP_DIR/"
fi
done
echo "Copia de seguridad completada."
Resumen
Ser consciente de las diferencias entre Dash y Bash y esforzarse por escribir código compatible con POSIX mejora drásticamente la portabilidad de tus scripts. Al principio puede parecer un poco incómodo, pero este esfuerzo extra evitará errores inesperados debidos a diferencias de entorno y te acercará un paso más a ser un 'programador competente'.
El contenido presentado aquí es básico pero muy importante. Anímate a ejecutar un script con #!/bin/sh en tu propio entorno. ¡Y experimenta la tranquilidad de saber que tu script funcionará en cualquier lugar!