En la Parte I aprendiste a usar la shell de forma interactiva: variables, redirecciones, tuberías y comodines. Ahora vas a dar el salto a la programación. Un shell script es un archivo de texto con comandos que la shell ejecuta secuencialmente, y dominar su escritura convierte a un usuario de Linux en un verdadero administrador de sistemas. En esta segunda parte aprenderás condicionales, bucles, funciones, arrays, procesamiento de texto y técnicas de depuración profesional.
📜 Tu primer shell script
Un shell script no es más que un archivo de texto plano que contiene una secuencia de comandos que la shell ejecuta de arriba abajo. La diferencia con escribir comandos uno a uno en la terminal es que el script los ejecuta todos automáticamente, y puedes reutilizarlo tantas veces como necesites.
Todo script de Bash profesional comienza con una línea especial llamada shebang (o hashbang): #!/bin/bash. Esta línea le indica al sistema operativo qué intérprete debe usar para ejecutar el archivo. Sin ella, el sistema usará la shell por defecto, que podría no ser Bash.
mi_primer_script.sh
#!/bin/bash# Mi primer script de Bash# Autor: Curso de Linux — Ciberaulaecho"¡Hola, mundo!"echo"Hoy es $(date '+%d de %B de %Y')"echo"Estás conectado como: $USER"echo"Tu directorio actual: $PWD"
Para ejecutar un script, necesitas darle permisos de ejecución con chmod y luego invocarlo con ./ (ruta relativa) o con su ruta absoluta:
terminal
# 1. Dar permisos de ejecuciónchmod +x mi_primer_script.sh
# 2. Ejecutar
./mi_primer_script.sh
# Alternativa: ejecutar con bash explícitamente (no necesita chmod)bash mi_primer_script.sh
✅ Buena práctica
Usa siempre #!/usr/bin/env bash en lugar de #!/bin/bash si quieres portabilidad. El comando env busca bash en el PATH del sistema, lo que funciona en más sistemas operativos (especialmente macOS, donde bash puede no estar en /bin/).
Ciclo de vida de un shell script: escritura, permisos, ejecución y resultado
📥 Argumentos y entrada de datos
Los scripts profesionales no tienen valores fijos «hardcodeados»: reciben datos del exterior. Bash ofrece dos mecanismos principales: argumentos posicionales (datos que se pasan al invocar el script) y lectura interactiva con el comando read.
argumentos.sh — Variables especiales
#!/bin/bash# Demostración de argumentos posicionalesecho"Nombre del script: $0"echo"Primer argumento: $1"echo"Segundo argumento: $2"echo"Todos los args: $@"echo"Número de args: $#"echo"PID del script: $$"echo"Último exit code: $?"# Uso: ./argumentos.sh hola mundo
Variable
Contenido
Ejemplo
$0
Nombre del script
./backup.sh
$1 a $9
Argumentos posicionales
$1 = primer argumento
${10}+
Argumentos de dos cifras (con llaves)
${12} = argumento 12
$#
Número total de argumentos
3 si pasas 3 args
$@
Todos los argumentos (como lista)
Ideal para iterar con for
$*
Todos los argumentos (como cadena única)
Se une con el primer carácter de IFS
$$
PID del proceso actual
12345
$?
Código de salida del último comando
0 = éxito, otro = error
lectura_interactiva.sh — Comando read
#!/bin/bash# Lectura básicaread -p "¿Cómo te llamas? " nombre
echo"Hola, $nombre"# Lectura con timeout (5 segundos)read -t 5 -p "Tienes 5 segundos: " respuesta
# Lectura silenciosa (contraseñas)read -s -p "Contraseña: " password
echo# Salto de línea tras input oculto# Lectura con valor por defectoread -p "Puerto [8080]: " puerto
puerto="${puerto:-8080}"# Si vacío, usa 8080
📸 Código en pantalla: la programación shell transforma comandos sueltos en automatizaciones potentes — Pexels (Licencia libre)
🔀 Condicionales: if, elif, else
Las estructuras condicionales permiten que tus scripts tomen decisiones. La sintaxis de if en Bash tiene una particularidad importante: la condición se evalúa con el comando test (equivalente a [ ]) o con el operador extendido [[ ]].
condicionales.sh
#!/bin/bash# ─── Comprobar si un archivo existe ───if [[ -f "/etc/passwd" ]]; thenecho"El archivo existe"elseecho"No se encontró el archivo"fi# ─── Comparar números ───
edad=25if [[ $edad -ge 18 ]]; thenecho"Mayor de edad"elif [[ $edad -ge 16 ]]; thenecho"Casi mayor de edad"elseecho"Menor de edad"fi# ─── Comparar cadenas ───read -p "¿Continuar? (s/n): " respuesta
if [[ "$respuesta" == "s" || "$respuesta" == "S" ]]; thenecho"Continuando..."fi
Operador
Tipo
Significado
Ejemplo
-eq, -ne
Numérico
Igual, no igual
[[ $a -eq 5 ]]
-lt, -le
Numérico
Menor, menor o igual
[[ $a -lt 10 ]]
-gt, -ge
Numérico
Mayor, mayor o igual
[[ $a -ge 0 ]]
==, !=
Cadena
Igual, diferente
[[ "$s" == "ok" ]]
-z, -n
Cadena
Vacía, no vacía
[[ -z "$var" ]]
-f
Archivo
Existe y es archivo regular
[[ -f "/etc/hosts" ]]
-d
Archivo
Existe y es directorio
[[ -d "/home" ]]
-r, -w, -x
Archivo
Legible, escribible, ejecutable
[[ -x "$script" ]]
&&, ||
Lógico
Y, O (dentro de [[ ]])
[[ $a -gt 0 && $a -lt 100 ]]
⚠️ Error frecuente
Siempre entrecomilla tus variables dentro de [ ] (test clásico): [ "$var" = "valor" ]. Sin comillas, si $var está vacía, Bash verá [ = "valor" ] y dará error de sintaxis. Con [[ ]] esto no es necesario, pero es buena costumbre igualmente.
Diagrama de flujo de un condicional if/elif/else en Bash
🎯 El comando case (selección múltiple)
Cuando necesitas comparar una variable contra múltiples valores posibles, case es más legible que una cadena de if/elif. Es el equivalente al switch de otros lenguajes:
menu.sh — Ejemplo de case
#!/bin/bashecho"=== Gestor de servicios ==="echo"1) Iniciar Apache"echo"2) Detener Apache"echo"3) Reiniciar Apache"echo"4) Estado de Apache"read -p "Opción: " opcion
case"$opcion"in1) sudosystemctl start apache2 ;;
2) sudo systemctl stop apache2 ;;
3) sudo systemctl restart apache2;;
4) systemctl status apache2 ;;
*) echo"Opción no válida"exit1 ;;
esac
💡 Patrones en case
Los patrones de case soportan comodines: [yYsS]) para capturar s/S/y/Y, *.txt) para archivos .txt, y ?) para un solo carácter. También puedes combinar patrones con |: start|iniciar).
🔁 Bucles: for, while y until
Los bucles permiten ejecutar un bloque de comandos repetidamente. Bash ofrece tres tipos, cada uno con su uso ideal:
Bucle for
Itera sobre una lista de elementos. Es el más versátil y el que usarás con más frecuencia:
bucles_for.sh
#!/bin/bash# Iterar sobre una lista literalfor fruta in manzana pera naranja; doecho"Fruta: $fruta"done# Iterar sobre un rango numéricofor i in {1..10}; doecho"Número: $i"done# Iterar sobre archivosfor archivo in /etc/*.conf; doecho"Config: $(basename "$archivo")"done# Bucle for estilo C (aritmético)for ((i=0; i<5; i++)); doecho"Iteración $i"done# Iterar sobre argumentos del scriptfor arg in"$@"; doecho"Procesando: $arg"done
Bucles while y until
bucles_while_until.sh
#!/bin/bash# while: se ejecuta MIENTRAS la condición sea verdadera
contador=1while [[ $contador -le 5 ]]; doecho"Contador: $contador"
((contador++))
done# until: se ejecuta HASTA QUE la condición sea verdadera
intentos=0until [[ $intentos -ge 3 ]]; doecho"Intento $((intentos + 1)) de 3"
((intentos++))
done# Leer un archivo línea a líneawhile IFS= read -r linea; doecho"→ $linea"done < /etc/hostname
# Bucle infinito con breakwhile true; doread -p "Escribe 'salir' para terminar: " input
[[ "$input" == "salir" ]] && breakdone
🧩 Funciones en Bash
Las funciones encapsulan bloques de código reutilizables. Son esenciales en cualquier script que supere las 30-40 líneas. En Bash, las funciones reciben argumentos posicionales ($1, $2...) igual que un script, pero dentro de la función estos se refieren a los argumentos de la función, no a los del script principal.
funciones.sh
#!/bin/bash# ─── Definir una función ───
saludar() {
local nombre="$1"# Variable locallocal hora=$(date +%H)
if [[ $hora -lt 12 ]]; thenecho"Buenos días, $nombre"elif [[ $hora -lt 20 ]]; thenecho"Buenas tardes, $nombre"elseecho"Buenas noches, $nombre"fi
}
# ─── Función con valor de retorno ───
archivo_existe() {
[[ -f "$1" ]] # Retorna 0 (true) o 1 (false)
}
# ─── Función que «devuelve» un valor vía echo ───
calcular_tamano() {
local tam=$(du -sh "$1"2>/dev/null | awk '{print $1}')
echo"$tam"
}
# ─── Uso ───
saludar "Ana"if archivo_existe "/etc/passwd"; then
tam=$(calcular_tamano "/etc/passwd")
echo"/etc/passwd ocupa $tam"fi
✅ Buena práctica
Declara siempre las variables internas de tus funciones con local. Sin local, las variables son globales al script y pueden causar colisiones difíciles de depurar. Es el equivalente a declarar variables con let o const en JavaScript.
📊 Arrays y arrays asociativos
Bash soporta arrays indexados (desde Bash 2.0) y arrays asociativos (desde Bash 4.0, equivalentes a diccionarios o mapas). Son ideales para almacenar colecciones de datos:
El procesamiento de texto es el alma de la administración de sistemas en Linux. Tres herramientas dominan este campo: cut para extraer columnas, awk para procesamiento avanzado por campos, y sed para buscar y reemplazar.
procesamiento_texto.sh
#!/bin/bash# ─── cut: extraer campos por delimitador ───# Obtener solo los nombres de usuario de /etc/passwd
cut -d: -f1 /etc/passwd | head -5
# ─── awk: el «navaja suiza» del texto ───# Procesos que consumen más de 1% de CPU
ps aux | awk '$3 > 1.0 {printf "%-15s %s%%\n", $11, $3}'# Sumar una columna de númerosecho -e "10\n20\n30\n40" | awk '{sum+=$1} END {print "Total:", sum}'# ─── sed: buscar y reemplazar ───# Reemplazar texto en un archivo (in-place)
sed -i 's/localhost/192.168.1.100/g' config.conf
# Eliminar líneas vacías
sed '/^$/d' archivo.txt
# Eliminar comentarios (líneas que empiezan por #)
sed '/^#/d' /etc/ssh/sshd_config
# ─── tr: transformar caracteres ───echo"hola mundo" | tr 'a-z''A-Z'# HOLA MUNDOecho"a::b::c" | tr -s ':'# a:b:c (squeeze)
Las cuatro herramientas fundamentales de procesamiento de texto en Linux
🔢 Aritmética y sustitución de comandos
Bash no es un lenguaje numérico, pero ofrece varias formas de realizar cálculos enteros y de capturar la salida de un comando dentro de una variable:
Las señales son mecanismos que el kernel usa para notificar a los procesos sobre eventos. Un script profesional debe manejar señales para limpiar archivos temporales, restaurar configuraciones o informar al usuario antes de terminar:
traps.sh — Manejo de señales
#!/bin/bash# Archivo temporal que debe eliminarse al terminar
TMPFILE=$(mktemp /tmp/mi_script.XXXXXX)
# Definir función de limpieza
limpiar() {
echo -e "\n⚠️ Señal recibida. Limpiando..."
rm -f "$TMPFILE"echo"✅ Temporal eliminado: $TMPFILE"exit0
}
# Capturar señales: SIGINT (Ctrl+C), SIGTERM (kill), EXITtrap limpiar SIGINT SIGTERM EXIT
# Simulación de trabajo largoecho"Trabajando con $TMPFILE..."echo"Pulsa Ctrl+C para interrumpir"for i in {1..30}; doecho"dato_$i" >> "$TMPFILE"
sleep 1done
Señal
Número
Cuándo se envía
Acción por defecto
SIGHUP
1
Se cierra la terminal
Terminar proceso
SIGINT
2
Ctrl+C
Terminar proceso
SIGTERM
15
kill (por defecto)
Terminar proceso
SIGKILL
9
kill -9
Terminar inmediatamente (no capturable)
EXIT
—
Script termina (normal o error)
Pseudoseñal de Bash
📸 Depuración de código: un buen script requiere tanto escritura como revisión — Pexels (Licencia libre)
🔍 Depuración profesional de scripts
Todo programador necesita herramientas de depuración. Bash ofrece varias opciones que, combinadas, convierten la caza de errores en un proceso sistemático en lugar de una búsqueda a ciegas.
cabecera_segura.sh — Plantilla profesional
#!/bin/bash# ─── Cabecera de seguridad (recomendada en TODO script) ───set -e # Salir inmediatamente si un comando fallaset -u # Error si se usa una variable no definidaset -o pipefail # Error si falla cualquier comando en una tubería# Equivalente compacto:# set -euo pipefail# ─── Activar modo depuración ───# set -x # Muestra cada comando antes de ejecutarlo# ─── Modo depuración desde fuera ───# bash -x mi_script.sh# ─── Depuración selectiva (solo una sección) ───echo"Inicio normal"set -x # Activar traza
resultado=$((2 + 3))
archivo="/etc/hosts"set +x # Desactivar trazaecho"Continúa sin traza"
Opción
Efecto
Cuándo usarla
set -e
Sale si cualquier comando retorna error (no 0)
Siempre en scripts de producción
set -u
Error al usar variables no definidas
Siempre. Evita errores silenciosos
set -o pipefail
La tubería falla si falla cualquier comando, no solo el último
Siempre con tuberías
set -x
Imprime cada comando antes de ejecutarlo (traza)
Desarrollo y depuración
bash -n script.sh
Verifica sintaxis sin ejecutar nada
Antes del primer test
Los cuatro niveles de depuración en Bash: de la verificación sintáctica al análisis estático
✅ Herramienta imprescindible: ShellCheck
ShellCheck es un analizador estático gratuito para scripts de Bash. Detecta errores comunes como variables sin comillas, uso incorrecto de test, y cientos de malas prácticas. Instálalo con sudo apt install shellcheck y ejecútalo con shellcheck mi_script.sh. También puedes usarlo online.
📝 Ejercicios prácticos
📋 Ejercicio 1: Script de backup
Crea un script backup.sh que reciba un directorio como argumento, compruebe que existe, y cree un archivo .tar.gz con la fecha en el nombre. Si no se pasa argumento, debe mostrar un mensaje de uso.
💡 Ver solución
backup.sh
#!/bin/bashset -euo pipefail
if [[ $# -eq 0 ]]; thenecho"Uso: $0 <directorio>"exit1fi
directorio="$1"if [[ ! -d "$directorio" ]]; thenecho"Error: '$directorio' no es un directorio"exit1fi
fecha=$(date +%Y%m%d_%H%M%S)
nombre_backup="backup_$(basename "$directorio")_${fecha}.tar.gz"
tar -czf "$nombre_backup""$directorio"echo"✅ Backup creado: $nombre_backup ($(du -h "$nombre_backup" | awk '{print $1}'))"
📋 Ejercicio 2: Menú interactivo con case
Crea un script que muestre un menú con 4 opciones de información del sistema (hostname, kernel, memoria, disco) y ejecute la opción elegida usando case.
💡 Ver solución
sysinfo.sh
#!/bin/bashecho"=== Información del sistema ==="echo"1) Hostname y usuario"echo"2) Información del kernel"echo"3) Uso de memoria"echo"4) Uso de disco"read -p "Elige (1-4): " op
case"$op"in1) echo"Host: $(hostname) | User: $USER" ;;
2) uname -a ;;
3) free -h ;;
4) df -h / ;;
*) echo"Opción no válida"; exit1 ;;
esac
📋 Ejercicio 3: Renombrado masivo con bucle
Escribe un script que recorra todos los archivos .txt del directorio actual y les añada un prefijo con la fecha de hoy (formato 20260227_archivo.txt).
Crea una función validar_email que reciba una cadena y compruebe si tiene formato de email válido (contiene @ y al menos un . después de la arroba). Úsala en un script que pida emails hasta que el usuario escriba «fin».
Escribe un script que analice /var/log/syslog (o /var/log/auth.log) y muestre: el número total de líneas, las 5 palabras más frecuentes en la tercera columna, y cuántas líneas contienen la palabra «error» (sin distinguir mayúsculas).
❓ Preguntas frecuentes sobre La Shell de Linux — Parte II: Programación Shell y control de flujo
Las dudas más comunes respondidas de forma clara y directa.
sh (Bourne Shell) es el estándar POSIX, compatible con casi cualquier sistema Unix. Bash extiende sh con arrays, aritmética avanzada, [[ ]] y más. Si necesitas portabilidad máxima, usa sh. Si trabajas en Linux, bash ofrece más herramientas.
Dos pasos: primero añade la línea shebang (#!/bin/bash) al inicio del archivo, y luego dale permisos de ejecución con chmod +x mi_script.sh. Después puedes ejecutarlo con ./mi_script.sh.
Cuando repitas lógica más de una vez, cuando el script supere las 50 líneas, o cuando quieras organizar el código en bloques reutilizables. Las funciones mejoran la legibilidad y facilitan la depuración.
[ ] es el comando test clásico compatible con sh. [[ ]] es una extensión de Bash que soporta operadores como =~, &&, || dentro de la expresión, y no requiere entrecomillar variables. Para scripts Bash, se recomienda [[ ]].
Usa set -x al inicio del script para ver cada comando antes de ejecutarse. Combínalo con set -e (salir ante errores) y set -u (error si usas variable no definida). También puedes ejecutar bash -x mi_script.sh desde fuera.
Sí, y es muy común. Por ejemplo: cat archivo.csv | sed "1d" | awk -F"," "{print $2}" elimina la cabecera con sed y extrae la segunda columna con awk. Combinar herramientas de texto es la esencia de la filosofía Unix.
★★★★★
Valora este artículo
¿Útil?
💬 Foro de discusión
¿Tienes dudas sobre La Shell de Linux — Parte II: Programación Shell y control de flujo? Comparte tu pregunta con la comunidad.
¿Tienes cuenta?o comenta como invitado ↓
Iniciar sesión
🔑 Recuperar contraseña
Introduce el email con el que te registraste. Te enviaremos un enlace para crear una nueva contraseña.
Crear cuenta
Solo necesitas nombre, email y contraseña. Sin verificación por email.
Todavía no hay mensajes. ¡Sé el primero en participar!
🚀 ¿Quieres dominar Linux profesionalmente?
Cursos bonificados por FUNDAE para empresas — formación 100% subvencionada
Usamos cookies propias para mejorar tu experiencia de navegación y analizar
el uso del sitio. No compartimos datos con terceros ni usamos cookies de
publicidad. Puedes aceptar todas, aceptar solo las necesarias o configurar
tus preferencias.
Política de privacidad
Imprescindibles para el funcionamiento del sitio: preferencias de interfaz,
gestión de sesiones y este mismo aviso de cookies. No recogen datos
identificativos.
Nos permiten entender cómo navegas por el contenido para mejorar la
experiencia de aprendizaje. Utilizan identificadores anónimos (UUID) sin
vinculación a datos personales. Retención máxima: 6 meses.
¿Cómo valorarías tu experiencia aprendiendo en esta sección?