control-de-flujo
Control de flujo
Section titled “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.
Condicionales
Section titled “Condicionales”Estructura
Section titled “Estructura”if [[ condición ]]; then # códigoelif [[ otra condición ]]; then # códigoelse # códigofiif 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.
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[[ ]] frente a [ ]
Section titled “[[ ]] frente a [ ]”[ ] |
[[ ]] |
|
|---|---|---|
| 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.
Comprobar si se es root
Section titled “Comprobar si se es root”if [[ $(id -u) -ne 0 ]]; then echo "Ejecuta el script como root" >&2 exit 1fiOperadores de test
Section titled “Operadores de test”Cadenas
Section titled “Cadenas”| 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 |
[[ $archivo == *.txt ]] # patrón[[ $entrada =~ ^[0-9]+$ ]] # solo dígitosNúmeros
Section titled “Números”| 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:
(( edad >= 18 )) && echo "Mayor de edad"No mezclar: == compara cadenas, -eq compara números.
Ficheros
Section titled “Ficheros”| 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 |
if [[ -f "$fichero" && -r "$fichero" ]]; then echo "Existe y se puede leer"fiComprobar variables definidas
Section titled “Comprobar variables definidas”[[ -v variable ]] # la variable existe (aunque esté vacía)[[ -z ${variable:-} ]] # vacía o sin definirOperadores lógicos
Section titled “Operadores lógicos”| 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) |
Dentro de la condición
Section titled “Dentro de la condición”if [[ $a -gt 0 && $a -lt 10 ]]; then echo "entre 1 y 9"; fiif [[ $op == "s" || $op == "S" ]]; then echo "sí"; fiif [[ ! -d "$dir" ]]; then echo "no es directorio"; fiEntre comandos
Section titled “Entre comandos”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 existeAdvertencia con a && b || c: c se ejecuta también si b falla, no solo si a falla. Para decisiones reales se usa if.
Agrupar comandos
Section titled “Agrupar comandos”[[ -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.
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.
read -rp "¿Continuar? (s/n) " rcase ${r,,} in s|si) echo "Continuando" ;; n|no) echo "Cancelado"; exit 0 ;; *) echo "Respuesta no válida"; exit 1 ;;esacBucle for
Section titled “Bucle for”Sobre una lista
Section titled “Sobre una lista”for nombre in ana luis marta; do echo "Hola, $nombre"doneSobre un rango
Section titled “Sobre un rango”for i in {1..5}; do echo "$i"; donefor i in {0..20..5}; do echo "$i"; done # con pasofor i in $(seq 1 5); do echo "$i"; done # alternativa con seqEstilo C
Section titled “Estilo C”for ((i = 0; i < 5; i++)); do echo "$i"doneSobre ficheros
Section titled “Sobre ficheros”for f in *.txt; do echo "Procesando $f"doneNo se debe iterar sobre $(ls): rompe con nombres con espacios. El globbing directo es la forma correcta.
Sobre un array o los argumentos
Section titled “Sobre un array o los argumentos”for elemento in "${array[@]}"; do echo "$elemento"; donefor arg in "$@"; do echo "$arg"; doneBucles while y until
Section titled “Bucles while y until”while repite mientras la condición sea verdadera; until repite hasta que lo sea.
contador=1while [[ $contador -le 5 ]]; do echo "$contador" ((contador++))done
until ping -c1 -W1 127.0.0.1 &>/dev/null; do echo "Esperando..." sleep 1doneBucle infinito
Section titled “Bucle infinito”while true; do # tarea periódica sleep 5doneLeer un fichero línea a línea
Section titled “Leer un fichero línea a línea”while IFS= read -r linea; do echo "Línea: $linea"done < fichero.txtIFS=conserva los espacios del principio y final.-revita 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).
Leer campos separados
Section titled “Leer campos separados”while IFS=: read -r usuario _ uid _; do echo "$usuario tiene UID $uid"done < /etc/passwdbreak, continue y códigos de salida
Section titled “break, continue y códigos de salida”break y continue
Section titled “break y continue”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"donebreak 2 sale de dos niveles de bucles anidados.
Códigos de salida
Section titled “Códigos de salida”Todo comando devuelve un número entre 0 y 255. Se consulta con $?.
ls /etc/hostsecho $? # 0
ls /no/existeecho $? # 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) |
exit y return
Section titled “exit y return”exit 0 # termina el script con éxitoexit 1 # termina con errorreturn 1 # sale de una función con código 1Un script sin exit explícito devuelve el código del último comando ejecutado.
Menús con select
Section titled “Menús con select”select genera un menú numerado y repite hasta que se hace break.
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" ;; esacdoneResultado:
1) iniciar2) parar3) estado4) salirElige una opción:PS3define el texto del prompt.- La opción elegida queda en la variable (
$opcion) y el número escrito en$REPLY. - Se combina con
casepara actuar según la elección. - Para menús con opciones por línea de comandos se usa
getopts(tema Scripts con opciones).