Curso de Shell Scripting

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.


18.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

18.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.


18.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.


18.4. Bashisms comunes a evitar (si necesitas POSIX)

BashismPOSIX 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 archivoUsa . archivo
declare o localUsa 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

18.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

18.6. Portabilidad en sustituciones de comandos

Malo:

1VAL=$(<archivo.txt)

👉 Algunos shells antiguos no soportan esa forma.

✅ Portátil:

1VAL=$(cat archivo.txt)

18.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.


18.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

18.9. Buenas prácticas para scripts portables

  1. ✅ Usa #!/bin/sh solo si el script es POSIX puro.
  2. ✅ Usa #!/usr/bin/env bash si dependes de características de Bash.
  3. ✅ Prueba con dash y checkbashisms para detectar problemas.
  4. ✅ Evita [[ ]], arrays, brace expansion, source.
  5. ✅ Usa solo comandos estándar: echo, printf, test, grep, awk, sed, cut, sort.
  6. ✅ Evita rutas absolutas si no son necesarias o hazlas configurables.
  7. ✅ Documenta las dependencias externas (por ejemplo, curl, jq).

18.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.


🏋️‍♂️ Ejercicio práctico 1

Crea un script contar_palabras.sh que:

  1. Sea 100% POSIX (usa #!/bin/sh).
  2. Acepte un archivo como argumento.
  3. Compruebe que el archivo existe.
  4. Muestre el número de palabras con wc -w.

Prueba que funciona tanto con bash como con dash.


🏋️‍♂️ Ejercicio práctico 2

Crea un script saludar.sh que:

  1. Sea portátil y compatible con /bin/sh.

  2. Acepte un nombre como argumento.

  3. Si no se pasa argumento, muestre:

    Uso: ./saludar.sh <nombre>
  4. Si recibe argumento, muestre:

    Hola, <nombre>

🏋️‍♂️ Ejercicio práctico 3

Crea un script procesa_archivos.sh que:

  1. Sea POSIX puro.
  2. Procese todos los archivos *.txt del directorio actual.
  3. Muestre el nombre y número de líneas de cada archivo.
  4. Use solo comandos POSIX (for, wc, echo).

✅ 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/sh o #!/usr/bin/env bash).
  • ✅ Usa herramientas como checkbashisms y pruebas con dash para garantizar portabilidad.
  • ✅ Documenta los requisitos (por ejemplo: “Requiere Bash ≥4.0” o “Compatible con POSIX”).
  • Loading...