Portabilidad y Bashisms
Muchos scripts en realidad se ejecutan con /bin/sh (el shell estándar POSIX), que no siempre es Bash.
El problema es que a menudo escribimos “Bashisms”: funciones o sintaxis que solo funcionan en Bash y rompen el script en otros shells.
1. POSIX vs Bash #
- POSIX sh: estándar compatible con la mayoría de sistemas (mínimo común).
- Bash: incluye muchas extensiones que no están en POSIX.
👉 Si tu script declara en la primera línea:
1#!/bin/sh
… deberías escribir solo sintaxis POSIX, no características propias de Bash.
👉 Si usas extensiones de Bash (arrays, [[ ]], brace expansion, etc.), declara explícitamente:
1#!/usr/bin/env bash
2. Identificar Bashisms #
Usa checkbashisms para encontrar código no POSIX:
En Debian/Ubuntu:
1sudo apt install devscripts 2checkbashisms script.sh
Ejemplo de salida:
possible bashism in script.sh line 3 (double brackets [[ ]])
👉 Esto te dice qué partes romperán en /bin/sh.
3. Pruebas rápidas con otros shells #
Ejecuta el script con dash (un shell POSIX rápido y estricto):
1dash script.sh
👉 Si falla, probablemente contiene Bashisms.
4. Bashisms comunes a evitar (si necesitas POSIX) #
| Bashism | POSIX equivalente |
|---|---|
[[ ... ]] | [ ... ] o test ... |
function foo {} | foo() { ... } |
Arrays arr=(a b c) | Usa variables separadas o archivos |
((a+b)) | Usa expr o $((a+b)) solo si es soportado |
echo {1..5} | Usa bucles for i in 1 2 3 4 5 |
source archivo | Usa . archivo |
declare o local | Usa variables simples o export |
Ejemplo no portátil: #
1#!/bin/sh 2for i in {1..5}; do 3 echo $i 4done
👉 Falla en dash.
✅ Solución POSIX:
1#!/bin/sh 2for i in 1 2 3 4 5; do 3 echo "$i" 4done
5. Portabilidad en expresiones condicionales #
Malo (Bash):
1if [[ "$VAR" == "hola" ]]; then 2 echo "ok" 3fi
✅ Portátil (POSIX):
1if [ "$VAR" = "hola" ]; then 2 echo "ok" 3fi
6. Portabilidad en sustituciones de comandos #
Malo:
1VAL=$(<archivo.txt)
👉 Algunos shells antiguos no soportan esa forma.
✅ Portátil:
1VAL=$(cat archivo.txt)
7. Portabilidad en variables #
Malo (Bash):
1VAR=${OTRA:-default}
👉 La expansión ${VAR:-valor} está disponible en Bash y en shells POSIX modernos, pero no en algunos antiguos.
⚠️ Si apuntas a sistemas muy viejos, evita expansiones complejas y usa condiciones tradicionales.
8. Portabilidad en bucles con read #
Malo (Bash con opciones avanzadas):
1while IFS= read -r -d '' line; do 2 echo "$line" 3done < archivo.txt
✅ Más portátil:
1while IFS= read line 2do 3 echo "$line" 4done < archivo.txt
9. Buenas prácticas para scripts portables #
- ✅ Usa
#!/bin/shsolo si el script es POSIX puro. - ✅ Usa
#!/usr/bin/env bashsi dependes de características de Bash. - ✅ Prueba con
dashycheckbashismspara detectar problemas. - ✅ Evita
[[ ]], arrays, brace expansion,source. - ✅ Usa solo comandos estándar:
echo,printf,test,grep,awk,sed,cut,sort. - ✅ Evita rutas absolutas si no son necesarias o hazlas configurables.
- ✅ Documenta las dependencias externas (por ejemplo,
curl,jq).
10. Ejemplo integrador: script portátil #
1#!/bin/sh 2 # script_portable.sh 3 4if [ $# -ne 1 ]; then 5 echo "Uso: $0 archivo" 6 exit 1 7fi 8 9ARCHIVO="$1" 10 11if [ ! -f "$ARCHIVO" ]; then 12 echo "Error: no existe el archivo $ARCHIVO" 13 exit 1 14fi 15 16LINEAS=$(wc -l < "$ARCHIVO") 17echo "El archivo tiene $LINEAS líneas"
👉 Compatible con Bash, Dash y la mayoría de shells POSIX.
11. Ejercicio práctico 1 #
Crea un script contar_palabras.sh que:
- Sea 100% POSIX (usa
#!/bin/sh). - Acepte un archivo como argumento.
- Compruebe que el archivo existe.
- Muestre el número de palabras con
wc -w.
Prueba que funciona tanto con bash como con dash.
12. Ejercicio práctico 2 #
Crea un script saludar.sh que:
-
Sea portátil y compatible con
/bin/sh. -
Acepte un nombre como argumento.
-
Si no se pasa argumento, muestre:
Uso: ./saludar.sh <nombre> -
Si recibe argumento, muestre:
Hola, <nombre>
13. Ejercicio práctico 3 #
Crea un script procesa_archivos.sh que:
- Sea POSIX puro.
- Procese todos los archivos
*.txtdel directorio actual. - Muestre el nombre y número de líneas de cada archivo.
- Use solo comandos POSIX (
for,wc,echo).
14. Buenas prácticas finales #
- ✅ Decide desde el inicio: ¿tu script será POSIX (máxima portabilidad) o Bash (más moderno y potente)?
- ✅ Declara el intérprete correcto en la shebang (
#!/bin/sho#!/usr/bin/env bash). - ✅ Usa herramientas como
checkbashismsy pruebas condashpara garantizar portabilidad. - ✅ Documenta los requisitos (por ejemplo: “Requiere Bash ≥4.0” o “Compatible con POSIX”).
Resumen del tema
Conceptos clave #
- Distinción entre POSIX Sh y Bash:
/bin/shapunta a shells ligeros y estrictos (comodashen Debian/Ubuntu) que no soportan extensiones no estándar. - Detección de Bashisms: auditoría con la utilidad
checkbashismsy validación directa ejecutando bajodash script.sh. - Equivalencias Portables POSIX:
- Reemplazar
[[ ... ]]por[ ... ](usando=en vez de==para comparar cadenas). - Reemplazar
sourcepor.. - Reemplazar
function name {}porname() { ... }. - Reemplazar expansiones de llaves
{1..5}por bucles explícitosfor i in 1 2 3 4 5.
- Reemplazar
- Regla de la Shebang: declarar estrictamente
#!/bin/shpara scripts POSIX y#!/usr/bin/env bashsi se usan arrays o características avanzadas.
Qué debes recordar #
Si declaras #!/bin/sh, evita características específicas de Bash y valida tu script con checkbashisms y dash para asegurar portabilidad universal.