Script bash para principiantes: primer script, variables, condiciones, bucles y cron
Un script bash es un archivo de texto con comandos de shell que empieza por la línea #!/bin/bash, recibe permiso de ejecución con chmod +x y se lanza como ./script.sh. Los scripts usan variables, los argumentos $1 y $2, condiciones if y bucles for y while; la ejecución periódica se configura con crontab -e.
Un script bash es un archivo de texto con comandos que, de otro modo, escribiría uno a uno en la terminal. El script empieza con la línea shebang #!/bin/bash, recibe permiso de ejecución con chmod +x y se lanza como ./script.sh. Esta guía de script bash para principiantes le acompaña desde la primera línea hasta la ejecución programada: variables, argumentos, condiciones, bucles, gestión de errores y cron.
Su primer script bash en un minuto
Cree un archivo en cualquier editor, por ejemplo nano:
nano hola.sh
#!/bin/bash
# Primer script: saludo e información básica del sistema
echo "Hola, $USER!"
echo "Hoy: $(date +%Y-%m-%d)"
echo "Está en: $(pwd)"
Guarde (Ctrl+O, Intro, Ctrl+X), dé permiso de ejecución y lance el script:
$ chmod +x hola.sh
$ ./hola.sh
Hola, alice!
Hoy: 2026-10-12
Está en: /home/alice
Las líneas que empiezan por # son comentarios. $(comando) inserta en la línea la salida de un comando.
Shebang: para qué sirve la primera línea
La línea #!/bin/bash indica al sistema con qué intérprete ejecutar el archivo. Sin ella, el script se ejecuta con la shell que esté disponible; en algunos sistemas /bin/sh no es bash, sino el más sencillo dash, donde algunas construcciones no funcionan.
También es habitual la variante #!/usr/bin/env bash: busca bash en el PATH, lo cual resulta útil si bash no está instalado en /bin.
Cómo ejecutar un script y por qué ./
Hay dos formas:
./hola.sh— necesita el shebang y el permisox. El prefijo./es obligatorio, porque el directorio actual no está en elPATH.bash hola.sh— no requiere permiso de ejecución, el intérprete se indica de forma explícita.
Si ve command not found sin ./, o Permission denied sin chmod +x, esas son exactamente las dos causas. Para llamar a sus scripts por nombre desde cualquier lugar, guárdelos en ~/bin o en /usr/local/bin.
Variables
Una variable se asigna sin espacios alrededor del = y se lee con $:
#!/bin/bash
nombre="copia"
directorio="/var/backups"
hoy=$(date +%F)
echo "Archivo: ${directorio}/${nombre}-${hoy}.tar.gz"
Las llaves separan el nombre de la variable del texto que la rodea. La línea nombre = "copia" con espacios es un error: bash intentará ejecutar un comando llamado nombre.
Las comillas importan
Entre comillas dobles las variables se sustituyen; entre comillas simples, no. Conviene casi siempre poner las variables entre comillas dobles, porque de lo contrario un nombre de archivo con espacios se dividirá en dos argumentos.
archivo="mi informe.txt"
rm $archivo # error: intentará borrar «mi» e «informe.txt»
rm "$archivo" # correcto
echo '$HOME' # muestra literalmente $HOME
echo "$HOME" # muestra /home/alice
Variables de entorno
Variables como $HOME, $USER y $PATH las define el sistema. Su propia variable se pasa a los procesos hijos con export. Los ajustes permanentes del entorno van en ~/.bashrc o ~/.profile.
Argumentos del script
Los valores que se pasan al lanzar el script están disponibles como $1, $2, etc.
| Variable | Qué contiene |
|---|---|
$0 | el nombre del script |
$1, $2… | el primer, el segundo argumento |
$# | el número de argumentos |
"$@" | todos los argumentos, cada uno por separado |
$? | el código de salida del último comando |
$$ | el PID del script actual |
#!/bin/bash
echo "El script $0 ha recibido $# argumento(s)"
echo "El primero: $1"
$ ./argumentos.sh nginx 80
El script ./argumentos.sh ha recibido 2 argumento(s)
El primero: nginx
La entrada del usuario se lee con el comando read: read -p "Introduzca el nombre: " nombre.
Condiciones if
Cada comando devuelve un código de salida: 0 significa éxito, cualquier otro valor un error. La construcción if comprueba precisamente ese código. Para las comparaciones se usa [[ ... ]]; los espacios dentro de los corchetes son obligatorios.
#!/bin/bash
if [[ $# -eq 0 ]]; then
echo "Uso: $0 <directorio>"
exit 1
fi
if [[ -d "$1" ]]; then
echo "El directorio $1 existe"
elif [[ -f "$1" ]]; then
echo "$1 es un archivo, no un directorio"
else
echo "$1 no encontrado"
fi
| Comprobación | Significado |
|---|---|
-f archivo | existe un archivo normal |
-d directorio | existe un directorio |
-e ruta | la ruta existe |
-x archivo | el archivo es ejecutable |
-z "$var" / -n "$var" | la cadena está vacía / no vacía |
"$a" == "$b", != | comparación de cadenas |
-eq -ne -lt -le -gt -ge | comparación numérica |
También se puede comprobar cualquier comando directamente:
if systemctl is-active --quiet nginx; then
echo "nginx está en marcha"
else
echo "nginx está detenido"
fi
Para muchas opciones resulta cómodo case:
case "$1" in
start) echo "Iniciando" ;;
stop) echo "Deteniendo" ;;
*) echo "Comando desconocido: $1"; exit 1 ;;
esac
Bucles for y while
for: recorrer una lista
for equipo in web1 web2 db1; do
echo "Comprobando $equipo"
ping -c 1 -W 1 "$equipo" > /dev/null && echo " accesible" || echo " inaccesible"
done
for archivo in /var/log/*.log; do
echo "$archivo: $(wc -l < "$archivo") líneas"
done
for i in {1..5}; do
echo "Intento $i"
done
while: mientras la condición se cumpla
Un ejemplo clásico es leer un archivo línea a línea:
while read -r usuario; do
echo "Usuario: $usuario"
done < usuarios.txt
Y esperar a que un servicio arranque:
contador=0
until curl -sf http://localhost:8080/health > /dev/null; do
contador=$((contador + 1))
if [[ $contador -ge 10 ]]; then
echo "El servicio no arrancó"
exit 1
fi
sleep 3
done
echo "El servicio está listo"
La aritmética en bash se escribe entre dobles paréntesis: $((a + b)). Solo funciona con números enteros.
Funciones
El código repetido se agrupa en funciones. Dentro de una función, los argumentos son los mismos $1, $2, y local evita que una variable salga de la función.
registro() {
local nivel="$1"; shift
echo "$(date '+%F %T') [$nivel] $*"
}
registro INFO "Comienza la copia de seguridad"
registro ERROR "Sin espacio en disco"
Gestión de errores: set -euo pipefail
Por defecto, bash continúa tras un comando fallido: un script de copia de seguridad puede terminar «con éxito» sin haber copiado nada. Por eso al inicio de los scripts se suele añadir un modo estricto:
#!/bin/bash
set -euo pipefail
-e— detenerse ante el primer error.-u— tratar el uso de una variable no definida como error (protege frente arm -rf "$DIR/"conDIRvacío).-o pipefail— una tubería falla si falla cualquiera de sus partes.
Para depurar, ejecute el script como bash -x script.sh: bash muestra cada comando antes de ejecutarlo. La utilidad shellcheck ayuda a encontrar errores típicos antes de lanzar el script.
Ejemplo práctico: un script de copia de seguridad
#!/bin/bash
set -euo pipefail
ORIGEN="${1:-/var/www}"
DESTINO="/var/backups/sitio"
DIAS_CONSERVAR=7
MARCA=$(date +%F_%H-%M)
mkdir -p "$DESTINO"
tar -czf "$DESTINO/sitio-$MARCA.tar.gz" -C "$(dirname "$ORIGEN")" "$(basename "$ORIGEN")"
echo "Archivo creado: $DESTINO/sitio-$MARCA.tar.gz"
# borrar las copias más antiguas que DIAS_CONSERVAR días
find "$DESTINO" -name "sitio-*.tar.gz" -mtime +"$DIAS_CONSERVAR" -delete
La expresión ${1:-/var/www} significa «el primer argumento, o /var/www si no hay ninguno». Guarde el script como /usr/local/bin/backup-sitio.sh y hágalo ejecutable: sudo chmod +x /usr/local/bin/backup-sitio.sh.
Ejecución programada: cron
Cron es el servicio que lanza comandos según un calendario. Las tareas de un usuario se editan con crontab -e, se listan con crontab -l, y las de root con sudo crontab -e.
Una línea de tarea consta de cinco campos de tiempo y el comando:
# minuto hora día_del_mes mes día_de_la_semana comando
30 2 * * * /usr/local/bin/backup-sitio.sh >> /var/log/backup.log 2>&1
| Programación | Cuándo se ejecuta |
|---|---|
*/5 * * * * | cada 5 minutos |
0 * * * * | al inicio de cada hora |
30 2 * * * | todos los días a las 02:30 |
0 9 * * 1-5 | de lunes a viernes a las 09:00 |
0 3 1 * * | el día 1 de cada mes a las 03:00 |
@reboot | una vez tras el arranque |
Por qué un script funciona a mano pero no en cron
- Entorno distinto. Cron ejecuta las tareas con un
PATHmínimo y sin su~/.bashrc. Escriba rutas completas a comandos y archivos, o definaPATHal inicio de la crontab. - Directorio actual distinto. La tarea arranca en el directorio personal, no donde está el script. Use rutas absolutas.
- El carácter %. En una crontab, el signo
%significa salto de línea, así quedate +%Fescrito directamente en la tarea debe escaparse como\%. Es más sencillo meter el comando en un script. - La salida se pierde. Redirija la salida a un registro (
>> archivo 2>&1); si no, no verá los errores. Las ejecuciones de cron se consultan conjournalctl -u cron(en sistemas tipo RHEL, el servicio se llamacrond).
En sistemas con systemd, los timers son una alternativa a cron: escriben la salida en el journal y pueden recuperar las ejecuciones perdidas.
Resumen
Un script bash para principiantes es un archivo que empieza con el shebang #!/bin/bash, tiene permiso de ejecución y contiene una secuencia de comandos. Añada variables entre comillas, una comprobación de argumentos con if, los bucles for y while y el modo estricto set -euo pipefail, y tendrá una herramienta de automatización fiable que cron puede ejecutar periódicamente. Los scripts bash, el entorno y cron se explican paso a paso y con práctica en nuestro curso de Linux.
Preguntas frecuentes
¿En qué se diferencia bash de sh?
sh es la shell POSIX estándar, mientras que bash es su versión ampliada, con arrays, dobles corchetes y otras comodidades. Un script que usa construcciones de bash debe ejecutarse con bash, no con sh.
¿Qué extensión debe tener un script bash?
Linux no mira la extensión: el intérprete lo determina el shebang. La extensión .sh es solo una convención para indicar a las personas que se trata de un script.
¿Cómo paso una contraseña a un script de forma segura?
No la pase como argumento: los argumentos son visibles para otros usuarios en ps. Use una variable de entorno cargada desde un archivo con permisos 600, o read -s para introducirla sin mostrarla.
¿Cómo compruebo que el script se ejecuta como root?
Compare el identificador de usuario con cero: if [[ $EUID -ne 0 ]]; then echo "Se requiere root"; exit 1; fi.
¿Cómo ejecuto un script en segundo plano para que no se detenga al cerrar la sesión SSH?
Use nohup ./script.sh &, una sesión de tmux o screen, y para un funcionamiento permanente convierta el script en un servicio systemd.
Este tema se explica a fondo y con práctica en el curso «Linux: с нуля до уверенного администрирования».
- Bash-скрипты: от команды в терминале до рабочего скрипта автоматизации
- Пакеты, окружение и первая автоматизация: apt, профиль и cron