Skip to content

control-de-flujo

Estructuras que deciden qué parte del código se ejecuta y cuántas veces: condicionales, operadores de test, case, bucles y menús.

Terminal window
if [[ condición ]]; then
# código
elif [[ otra condición ]]; then
# código
else
# código
fi

if evalúa el código de salida de un comando: 0 se considera verdadero, cualquier otro valor falso. Por eso funciona con cualquier comando, no solo con tests.

Terminal window
if grep -q "root" /etc/passwd; then
echo "root existe"
fi
if ! ping -c1 -W1 127.0.0.1 &>/dev/null; then
echo "Sin respuesta"
fi
[ ] [[ ]]
Tipo Comando (test), POSIX Palabra clave de Bash
Variables sin comillas Pueden romper la expresión Seguras
&&, || No (usa -a, -o) Sí
Patrones (== *.txt) y regex (=~) No Sí

Se recomienda [[ ]] en scripts de Bash. Los espacios dentro de los corchetes son obligatorios.

Terminal window
if [[ $(id -u) -ne 0 ]]; then
echo "Ejecuta el script como root" >&2
exit 1
fi
Operador Significado
== / != Igual / distinto (acepta patrones con *, ?)
-z "$v" Cadena vacía
-n "$v" Cadena no vacía
< / > Orden alfabético (dentro de [[ ]])
=~ Coincide con expresión regular
Terminal window
[[ $archivo == *.txt ]] # patrón
[[ $entrada =~ ^[0-9]+$ ]] # solo dígitos
Operador Significado
-eq Igual
-ne Distinto
-gt / -ge Mayor / mayor o igual
-lt / -le Menor / menor o igual

También se puede usar (( )), más natural para aritmética:

Terminal window
(( edad >= 18 )) && echo "Mayor de edad"

No mezclar: == compara cadenas, -eq compara números.

Operador Significado
-e f Existe
-f f Es fichero regular
-d f Es directorio
-L f Es enlace simbólico
-r f / -w f / -x f Permiso de lectura / escritura / ejecución
-s f Existe y no está vacío
f1 -nt f2 f1 es más reciente que f2
Terminal window
if [[ -f "$fichero" && -r "$fichero" ]]; then
echo "Existe y se puede leer"
fi
Terminal window
[[ -v variable ]] # la variable existe (aunque esté vacía)
[[ -z ${variable:-} ]] # vacía o sin definir
Operador Significado
! Negación
&& Y (ejecuta el siguiente solo si el anterior tuvo éxito)
|| O (ejecuta el siguiente solo si el anterior falló)
; Separador (ejecuta siempre el siguiente)
Terminal window
if [[ $a -gt 0 && $a -lt 10 ]]; then echo "entre 1 y 9"; fi
if [[ $op == "s" || $op == "S" ]]; then echo "sí"; fi
if [[ ! -d "$dir" ]]; then echo "no es directorio"; fi
Terminal window
mkdir datos && cd datos # entra solo si se creó
cd "$dir" || exit 1 # sale si falla el cd
[[ -f config ]] && source config # carga solo si existe

Advertencia con a && b || c: c se ejecuta también si b falla, no solo si a falla. Para decisiones reales se usa if.

Terminal window
[[ -f "$f" ]] || { echo "No existe $f" >&2; exit 1; }

Las llaves requieren un espacio tras { y un ; antes de }.

Compara una variable con varios patrones. Es más legible que encadenar elif.

Terminal window
case $opcion in
start) echo "Iniciando" ;;
stop|halt) echo "Parando" ;;
[0-9]) echo "Un dígito" ;;
*.txt) echo "Fichero de texto" ;;
*) echo "Opción no válida" ;;
esac
  • Cada bloque termina con ;;.
  • | separa alternativas.
  • Admite patrones de globbing.
  • *) es el caso por defecto y va al final.

Ejemplo habitual: procesar una respuesta del usuario.

Terminal window
read -rp "¿Continuar? (s/n) " r
case ${r,,} in
s|si) echo "Continuando" ;;
n|no) echo "Cancelado"; exit 0 ;;
*) echo "Respuesta no válida"; exit 1 ;;
esac
Terminal window
for nombre in ana luis marta; do
echo "Hola, $nombre"
done
Terminal window
for i in {1..5}; do echo "$i"; done
for i in {0..20..5}; do echo "$i"; done # con paso
for i in $(seq 1 5); do echo "$i"; done # alternativa con seq
Terminal window
for ((i = 0; i < 5; i++)); do
echo "$i"
done
Terminal window
for f in *.txt; do
echo "Procesando $f"
done

No se debe iterar sobre $(ls): rompe con nombres con espacios. El globbing directo es la forma correcta.

Terminal window
for elemento in "${array[@]}"; do echo "$elemento"; done
for arg in "$@"; do echo "$arg"; done

while repite mientras la condición sea verdadera; until repite hasta que lo sea.

Terminal window
contador=1
while [[ $contador -le 5 ]]; do
echo "$contador"
((contador++))
done
until ping -c1 -W1 127.0.0.1 &>/dev/null; do
echo "Esperando..."
sleep 1
done
Terminal window
while true; do
# tarea periódica
sleep 5
done
Terminal window
while IFS= read -r linea; do
echo "Línea: $linea"
done < fichero.txt
  • IFS= conserva los espacios del principio y final.
  • -r evita interpretar las barras invertidas.

Atención con cat fichero | while ...: el bucle se ejecuta en una subshell y las variables modificadas dentro no se ven fuera. La redirección done < fichero evita el problema. Si la entrada es la salida de un comando, se usa done < <(comando).

Terminal window
while IFS=: read -r usuario _ uid _; do
echo "$usuario tiene UID $uid"
done < /etc/passwd
Terminal window
for i in {1..10}; do
[[ $i -eq 3 ]] && continue # salta a la siguiente iteración
[[ $i -eq 6 ]] && break # sale del bucle
echo "$i"
done

break 2 sale de dos niveles de bucles anidados.

Todo comando devuelve un número entre 0 y 255. Se consulta con $?.

Terminal window
ls /etc/hosts
echo $? # 0
ls /no/existe
echo $? # 2
Código Significado habitual
0 Éxito
1 Error general
2 Uso incorrecto del comando
126 Comando encontrado pero no ejecutable
127 Comando no encontrado
130 Terminado con Ctrl+C (128 + señal 2)
Terminal window
exit 0 # termina el script con éxito
exit 1 # termina con error
return 1 # sale de una función con código 1

Un script sin exit explícito devuelve el código del último comando ejecutado.

select genera un menú numerado y repite hasta que se hace break.

Terminal window
PS3="Elige una opción: "
select opcion in iniciar parar estado salir; do
case $opcion in
iniciar) echo "Iniciando..." ;;
parar) echo "Parando..." ;;
estado) echo "Todo correcto" ;;
salir) break ;;
*) echo "Opción no válida" ;;
esac
done

Resultado:

1) iniciar
2) parar
3) estado
4) salir
Elige una opción:
  • PS3 define el texto del prompt.
  • La opción elegida queda en la variable ($opcion) y el número escrito en $REPLY.
  • Se combina con case para actuar según la elección.
  • Para menús con opciones por línea de comandos se usa getopts (tema Scripts con opciones).