Scripting Básico

Ahora que dominas variables, I/O y expansiones en la terminal, es hora de guardar todo en archivos: los scripts.
¿Qué es un Script?
Un script es un archivo de texto con comandos que se ejecutan en secuencia:
# En lugar de escribir esto cada vez:
echo "Hola"
date
whoami
# Lo guardas en un archivo y lo ejecutas cuando quieras
Tu Primer Script
Paso 1: Crear el archivo
# Crea el archivo con nano o cualquier editor
nano mi_primer_script.sh
Paso 2: Agregar el shebang y comandos
#!/bin/bash
# Mi primer script
echo "¡Hola desde mi script!"
echo "Fecha: $(date)"
echo "Usuario: $USER"
echo "Directorio: $PWD"
Paso 3: Hacerlo ejecutable
# Ver permisos actuales
ls -l mi_primer_script.sh
# -rw-r--r-- ... (no tiene permiso de ejecución)
# Dar permiso de ejecución
chmod +x mi_primer_script.sh
# Verificar
ls -l mi_primer_script.sh
# -rwxr-xr-x ... (ahora tiene la x)
Paso 4: Ejecutar
# Forma 1: Con ./
./mi_primer_script.sh
# Forma 2: Con bash explícito
bash mi_primer_script.sh
# 1. Crea el archivo
cat << 'EOF' > hola.sh
#!/bin/bash
echo "==========================="
echo " INFORMACIÓN DEL SISTEMA"
echo "==========================="
echo "Usuario: $USER"
echo "Fecha: $(date)"
echo "Directorio: $(pwd)"
echo "Shell: $SHELL"
echo "==========================="
EOF
# 2. Hazlo ejecutable
chmod +x hola.sh
# 3. Ejecútalo
./hola.sh
El Shebang: #!/bin/bash
¿Qué es el shebang?
El shebang (también llamado hashbang) es la primera línea de un script que le dice al sistema qué programa debe interpretar el archivo.
#!/bin/bash
││ └──────── Ruta al intérprete (el programa que ejecutará el script)
│└── ! (bang)
└── # (hash/sharp)
hash + bang = shebang
¿Cómo funciona?
Cuando ejecutas ./mi_script.sh, el sistema operativo:
- Lee los primeros bytes del archivo
- Busca
#!al inicio - Lee la ruta que sigue (ej:
/bin/bash) - Ejecuta ese programa pasándole el script como argumento
Tu comando: ./mi_script.sh
Lo que hace Linux: /bin/bash ./mi_script.sh
Es como si el shebang dijera: “Para leer este archivo, usa el programa /bin/bash”.
¿Qué pasa SIN shebang?
Si tu script no tiene shebang y lo ejecutas con ./script.sh:
# Script sin shebang
echo "Hola"
date
Resultado: El sistema intenta ejecutarlo con el shell por defecto (generalmente /bin/sh). Puede funcionar para comandos simples, pero:
- Algunas características de Bash no funcionarán en
/bin/sh - El comportamiento puede variar entre sistemas
- Es impredecible - mala práctica
Solución: Siempre incluir shebang.
¿Qué pasa si la ruta es incorrecta?
#!/bin/programa_que_no_existe
echo "Hola"
./script.sh
# bash: ./script.sh: /bin/programa_que_no_existe: bad interpreter: No such file or directory
El sistema no puede encontrar el intérprete → error.
Shebangs comunes
| Shebang | Usa | Cuándo usarlo |
|---|---|---|
#!/bin/bash |
Bash directamente | Cuando sabes que Bash está en /bin/bash |
#!/bin/sh |
Shell POSIX básico | Scripts muy simples y portables |
#!/usr/bin/env bash |
Busca Bash en PATH | Recomendado - más portable |
#!/usr/bin/env python3 |
Busca Python3 | Scripts de Python |
¿Por qué /usr/bin/env?
El problema con #!/bin/bash:
- En Linux, Bash suele estar en
/bin/bash - En macOS, puede estar en
/usr/local/bin/bash(si instalaste con Homebrew) - En otros sistemas, puede variar
/usr/bin/env bash resuelve esto:
#!/usr/bin/env bash
│ │
│ └── "busca un programa llamado bash"
└── "usa el comando env para..."
env busca bash en tu $PATH y lo ejecuta. Funciona sin importar dónde esté instalado.
Dos formas de ejecutar scripts
| Método | Necesita shebang | Necesita chmod +x |
|---|---|---|
./script.sh |
Sí | Sí |
bash script.sh |
No | No |
# Forma 1: Ejecutar directamente (usa el shebang)
chmod +x script.sh
./script.sh
# Forma 2: Llamar al intérprete explícitamente (ignora el shebang)
bash script.sh
# 1. Script con shebang correcto
cat << 'EOF' > test1.sh
#!/bin/bash
echo "Shell: $BASH_VERSION"
EOF
chmod +x test1.sh
./test1.sh
# 2. Script SIN shebang
cat << 'EOF' > test2.sh
echo "Sin shebang"
echo "Shell: $BASH_VERSION"
EOF
chmod +x test2.sh
./test2.sh
# ¿Funciona? ¿Muestra BASH_VERSION?
# 3. Script con shebang incorrecto
cat << 'EOF' > test3.sh
#!/bin/programa_falso
echo "Esto no se ejecutará"
EOF
chmod +x test3.sh
./test3.sh
# ¿Qué error da?
# 4. Usar env (portable)
cat << 'EOF' > test4.sh
#!/usr/bin/env bash
echo "Bash encontrado en: $(which bash)"
EOF
chmod +x test4.sh
./test4.sh
# Limpia
rm test1.sh test2.sh test3.sh test4.sh
Argumentos del Script
Los scripts pueden recibir argumentos de la línea de comandos:
./mi_script.sh argumento1 argumento2 argumento3
Variables especiales de argumentos
| Variable | Contenido |
|---|---|
$0 |
Nombre del script |
$1 |
Primer argumento |
$2 |
Segundo argumento |
$n |
N-ésimo argumento |
$# |
Número de argumentos |
$@ |
Todos los argumentos (como lista) |
$* |
Todos los argumentos (como string) |
#!/bin/bash
# argumentos.sh
echo "Script: $0"
echo "Primer argumento: $1"
echo "Segundo argumento: $2"
echo "Número de argumentos: $#"
echo "Todos: $@"
chmod +x argumentos.sh
./argumentos.sh hola mundo 123
# Script: ./argumentos.sh
# Primer argumento: hola
# Segundo argumento: mundo
# Número de argumentos: 3
# Todos: hola mundo 123
# Crea saludar.sh
cat << 'EOF' > saludar.sh
#!/bin/bash
# Uso: ./saludar.sh nombre
if [ -z "$1" ]; then
echo "Uso: $0 nombre"
exit 1
fi
echo "¡Hola, $1!"
echo "Bienvenido al sistema."
EOF
chmod +x saludar.sh
# Prueba
./saludar.sh
./saludar.sh Juan
./saludar.sh "María García"
Códigos de Salida
Cada comando termina con un código de salida:
| Código | Significado |
|---|---|
0 |
Éxito |
1-255 |
Error (el número indica el tipo) |
# Ver código del último comando
ls /tmp
echo $? # 0 (éxito)
ls /directorio_falso
echo $? # 2 (error)
En scripts
#!/bin/bash
# Salir con éxito
exit 0
# Salir con error
exit 1
# Ejecuta y observa $?
true
echo $? # 0
false
echo $? # 1
ls /tmp
echo $? # 0
ls /noexiste
echo $? # 2
Condicionales: if
Sintaxis básica
if [ condición ]; then
# código si es verdadero
fi
Con else
if [ condición ]; then
# código si verdadero
else
# código si falso
fi
Con elif
if [ condición1 ]; then
# código
elif [ condición2 ]; then
# código
else
# código
fi
Ejemplo
#!/bin/bash
edad=$1
if [ -z "$edad" ]; then
echo "Uso: $0 edad"
exit 1
fi
if [ $edad -ge 18 ]; then
echo "Eres mayor de edad"
else
echo "Eres menor de edad"
fi
Condiciones de Test: [ ] y [[ ]]
Comparación de números
| Operador | Significado |
|---|---|
-eq |
Igual (equal) |
-ne |
No igual (not equal) |
-lt |
Menor que (less than) |
-le |
Menor o igual (less or equal) |
-gt |
Mayor que (greater than) |
-ge |
Mayor o igual (greater or equal) |
a=5
b=10
[ $a -eq $b ] # falso
[ $a -lt $b ] # verdadero
[ $a -ne $b ] # verdadero
Comparación de strings
| Operador | Significado |
|---|---|
= |
Iguales |
!= |
Diferentes |
-z |
Vacío (zero length) |
-n |
No vacío (non-zero) |
nombre="Juan"
[ "$nombre" = "Juan" ] # verdadero
[ "$nombre" != "Pedro" ] # verdadero
[ -z "$nombre" ] # falso (no está vacío)
[ -n "$nombre" ] # verdadero (no está vacío)
Test de archivos
| Operador | Significado |
|---|---|
-f |
Es un archivo regular |
-d |
Es un directorio |
-e |
Existe |
-r |
Es legible (readable) |
-w |
Es escribible (writable) |
-x |
Es ejecutable |
[ -f "/etc/passwd" ] # verdadero
[ -d "/home" ] # verdadero
[ -e "/tmp" ] # verdadero
[ -x "./script.sh" ] # depende de los permisos
# Crea verificar.sh
cat << 'EOF' > verificar.sh
#!/bin/bash
archivo=$1
if [ -z "$archivo" ]; then
echo "Uso: $0 archivo"
exit 1
fi
if [ -e "$archivo" ]; then
echo "$archivo existe"
if [ -f "$archivo" ]; then
echo " Es un archivo"
elif [ -d "$archivo" ]; then
echo " Es un directorio"
fi
if [ -r "$archivo" ]; then
echo " Es legible"
fi
if [ -w "$archivo" ]; then
echo " Es escribible"
fi
else
echo "$archivo NO existe"
fi
EOF
chmod +x verificar.sh
# Prueba
./verificar.sh /etc/passwd
./verificar.sh /home
./verificar.sh /archivo_falso
Loops: for y while
Loop for
# Iterar sobre una lista
for fruta in manzana naranja pera; do
echo "Fruta: $fruta"
done
# Iterar sobre archivos
for archivo in *.txt; do
echo "Archivo: $archivo"
done
# Iterar sobre números
for i in {1..5}; do
echo "Número: $i"
done
# Estilo C
for ((i=1; i<=5; i++)); do
echo "Número: $i"
done
Loop while
# Mientras la condición sea verdadera
contador=1
while [ $contador -le 5 ]; do
echo "Contador: $contador"
contador=$((contador + 1))
done
# Crea contador.sh
cat << 'EOF' > contador.sh
#!/bin/bash
# Cuenta desde 1 hasta el argumento
limite=${1:-10}
echo "Contando hasta $limite:"
for i in $(seq 1 $limite); do
echo $i
done
echo "¡Terminado!"
EOF
chmod +x contador.sh
./contador.sh 5
./contador.sh
Script Completo de Ejemplo
Aquí un script que combina todo lo aprendido:
#!/bin/bash
#
# backup.sh - Script de backup simple
# Uso: ./backup.sh [directorio_origen] [directorio_destino]
#
# Valores por defecto
ORIGEN=${1:-$HOME}
DESTINO=${2:-/tmp/backups}
FECHA=$(date +%Y%m%d_%H%M%S)
NOMBRE_BACKUP="backup_$(whoami)_$FECHA"
# Función para mostrar uso
mostrar_uso() {
echo "Uso: $0 [origen] [destino]"
echo " origen: Directorio a respaldar (default: \$HOME)"
echo " destino: Dónde guardar backup (default: /tmp/backups)"
}
# Verificar que origen existe
if [ ! -d "$ORIGEN" ]; then
echo "Error: El directorio origen '$ORIGEN' no existe"
mostrar_uso
exit 1
fi
# Crear directorio destino si no existe
if [ ! -d "$DESTINO" ]; then
echo "Creando directorio destino: $DESTINO"
mkdir -p "$DESTINO"
fi
# Mostrar información
echo "==================================="
echo " BACKUP SCRIPT"
echo "==================================="
echo "Origen: $ORIGEN"
echo "Destino: $DESTINO/$NOMBRE_BACKUP.tar.gz"
echo "Fecha: $(date)"
echo "==================================="
# Confirmar
read -p "¿Continuar? (s/n): " respuesta
if [ "$respuesta" != "s" ]; then
echo "Backup cancelado."
exit 0
fi
# Crear backup
echo "Creando backup..."
tar -czf "$DESTINO/$NOMBRE_BACKUP.tar.gz" -C "$(dirname $ORIGEN)" "$(basename $ORIGEN)" 2>/dev/null
# Verificar resultado
if [ $? -eq 0 ]; then
tamaño=$(ls -lh "$DESTINO/$NOMBRE_BACKUP.tar.gz" | awk '{print $5}')
echo "¡Backup completado!"
echo "Archivo: $DESTINO/$NOMBRE_BACKUP.tar.gz"
echo "Tamaño: $tamaño"
exit 0
else
echo "Error al crear el backup"
exit 1
fi
# 1. Crea el script (copia el código de arriba)
nano backup.sh
# 2. Hazlo ejecutable
chmod +x backup.sh
# 3. Prueba (con valores por defecto)
./backup.sh
# 4. Prueba con argumentos
mkdir -p ~/test_backup
echo "archivo de prueba" > ~/test_backup/prueba.txt
./backup.sh ~/test_backup /tmp/mis_backups
# 5. Verifica el backup
ls -la /tmp/mis_backups/
Debugging de Scripts
Modo verbose: bash -v
bash -v mi_script.sh
# Muestra cada línea antes de ejecutarla
Modo trace: bash -x
bash -x mi_script.sh
# Muestra cada comando expandido
Dentro del script
#!/bin/bash
set -x # Activar trace
# ... código ...
set +x # Desactivar trace
Buenas Prácticas
- Siempre incluir shebang:
#!/bin/bash - Comentar el código: explicar qué hace
- Validar argumentos: verificar que existen y son válidos
- Usar valores por defecto:
${VAR:-default} - Verificar códigos de salida:
$? - Citar variables:
"$variable"para evitar problemas con espacios - Usar
set -e: terminar si un comando falla
#!/bin/bash
set -e # Salir si hay error
set -u # Error si variable no definida
Resumen
| Concepto | Sintaxis |
|---|---|
| Shebang | #!/bin/bash |
| Ejecutar | chmod +x script.sh && ./script.sh |
| Argumentos | $1, $2, $@, $# |
| Código salida | $?, exit 0, exit 1 |
| Condicional | if [ condición ]; then ... fi |
| Loop for | for i in lista; do ... done |
| Loop while | while [ cond ]; do ... done |
| Test archivo | -f, -d, -e, -r, -w |
| Test número | -eq, -lt, -gt, -ne |
| Test string | =, !=, -z, -n |
¿Qué sigue?
Has aprendido los fundamentos de Bash como lenguaje:
- Variables y su sintaxis
- Variables de entorno
- Entrada/salida
- Expansiones y sustituciones
- Scripting básico
Ahora puedes:
- Automatizar tareas repetitivas
- Crear herramientas personalizadas
- Entender scripts que encuentres
- Continuar aprendiendo temas avanzados (arrays, funciones complejas, regex)
¡Felicidades! Has completado el módulo de Bash.