Introduccion a Shell Script

Principiante | 60 min de lectura | 2025.12.02

Lo que aprenderas en este tutorial

  • Estructura basica de scripts de shell
  • Uso de variables
  • Condicionales (sentencia if)
  • Bucles (for, while)
  • Scripts de automatizacion practicos

¿Que es un script de shell? ¿Por que aprenderlo?

El nacimiento de los scripts de shell

La historia de los scripts de shell se remonta a 1971 con Thompson shell (sh). En 1979, Stephen Bourne desarrollo Bourne Shell (sh), que se convirtio en la base de los scripts de shell modernos.

En 1989, Brian Fox desarrollo Bash (Bourne Again Shell) como parte del proyecto GNU. Bash es actualmente el shell mas utilizado.

“Los scripts de shell son la forma mas conveniente de automatizar tareas repetitivas” — Sabiduria ancestral del entorno Unix

¿Por que aprender scripts de shell?

  1. Automatizacion: Automatiza tareas repetitivas y ahorra tiempo
  2. Administracion de sistemas: Esencial para gestion de servidores, despliegues y backups
  3. CI/CD: Ampliamente utilizado en GitHub Actions, Jenkins, etc.
  4. Portabilidad: Funciona en casi todos los sistemas Unix

Bash y otros shells

ShellCaracteristicasUso principal
bashEl mas extendido. Compatible con POSIX + extensionesScripts generales
shEstandar POSIX. Funcionalidad minimaScripts con enfoque en portabilidad
zshCompatible con bash + potente autocompletadoShell interactivo
fishAmigable para el usuarioShell interactivo
dashLigero y rapidoScripts del sistema

Mejores practicas: Usa #!/bin/sh cuando la portabilidad es importante, y #!/bin/bash cuando uses caracteristicas especificas de Bash.

Tu primer script de shell

Comencemos con “Hello World”.

hello.sh

#!/bin/bash

# Este es tu primer script de shell
echo "Hello, World!"

Metodo de ejecucion

# Otorgar permisos de ejecucion
chmod +x hello.sh

# Ejecutar
./hello.sh

# O, especificar bash explicitamente
bash hello.sh

Sobre el Shebang

La primera linea #!/bin/bash se llama shebang y especifica que interprete usara para ejecutar este script.

#!/bin/bash     # Ejecutar con bash
#!/bin/sh       # Ejecutar con POSIX sh (alta portabilidad)
#!/usr/bin/env bash  # Buscar bash en PATH y ejecutar (recomendado)

Documentacion oficial: GNU Bash Manual

Uso de variables

Variables basicas

#!/bin/bash

# Definicion de variables (¡no pongas espacios alrededor del =!)
name="Taro"
age=25

# Referencia de variables
echo "Nombre: $name"
echo "Edad: ${age} anos"

# Asignar resultado de comando a variable
current_date=$(date +%Y-%m-%d)
echo "Fecha de hoy: $current_date"

# Sintaxis antigua (backticks) - no recomendada
old_style=`date +%Y-%m-%d`

Error comun: Espacios

# Incorrecto (error si hay espacios)
name = "Taro"    # command not found: name
name= "Taro"     # Variable vacia ejecutando comando "Taro"

# Correcto
name="Taro"

Alcance de variables

#!/bin/bash

# Variable global
global_var="I am global"

function example() {
    # Variable local (solo valida dentro de la funcion)
    local local_var="I am local"
    echo "$local_var"
    echo "$global_var"
}

example
echo "$local_var"   # Vacio (no visible fuera de la funcion)

Variables especiales

#!/bin/bash

echo "Nombre del script: $0"
echo "Primer argumento: $1"
echo "Segundo argumento: $2"
echo "Numero de argumentos: $#"
echo "Todos los argumentos: $@"
echo "Todos los argumentos (cadena): $*"
echo "Estado de salida del comando anterior: $?"
echo "ID del proceso actual: $$"
echo "PID del proceso en segundo plano: $!"

Ejemplo de ejecucion:

$ ./script.sh arg1 arg2 arg3
Nombre del script: ./script.sh
Primer argumento: arg1
Segundo argumento: arg2
Numero de argumentos: 3
Todos los argumentos: arg1 arg2 arg3

Variables de entorno

# Referencia de variables de entorno
echo "Directorio home: $HOME"
echo "Nombre de usuario: $USER"
echo "Directorio actual: $PWD"
echo "Shell: $SHELL"
echo "Path: $PATH"

# Configurar variable de entorno (heredada por subprocesos)
export MY_VAR="some value"

# Configurar variable de entorno temporalmente y ejecutar comando
DEBUG=true ./my_script.sh

Diferencias entre comillas

name="World"

# Comillas dobles: las variables se expanden
echo "Hello, $name"    # Hello, World

# Comillas simples: salida literal
echo 'Hello, $name'    # Hello, $name

# Escapar con backslash
echo "Hello, \$name"   # Hello, $name

Condicionales (sentencia if)

Sintaxis basica

#!/bin/bash

age=20

if [ $age -ge 20 ]; then
    echo "Es adulto"
elif [ $age -ge 13 ]; then
    echo "Es adolescente"
else
    echo "Es nino"
fi

Tipos de sintaxis de prueba

# [ ] - Sintaxis test clasica (compatible con POSIX)
if [ $a -eq $b ]; then

# [[ ]] - Sintaxis extendida de Bash (recomendada)
if [[ $a == $b ]]; then

# (( )) - Evaluacion aritmetica
if (( a > b )); then

Mejores practicas: Se recomienda [[ ]] cuando uses Bash. Permite coincidencia de patrones y expresiones regulares.

Operadores de comparacion numerica

OperadorSignificadoEjemplo
-eqIgual (equal)[ $a -eq $b ]
-neNo igual (not equal)[ $a -ne $b ]
-ltMenor que (less than)[ $a -lt $b ]
-leMenor o igual (less or equal)[ $a -le $b ]
-gtMayor que (greater than)[ $a -gt $b ]
-geMayor o igual (greater or equal)[ $a -ge $b ]

Comparacion de cadenas

#!/bin/bash

str="hello"

# Comparacion de cadenas (¡es importante encerrar entre comillas!)
if [ "$str" = "hello" ]; then
    echo "Las cadenas coinciden"
fi

# Verificar cadena vacia
if [ -z "$str" ]; then
    echo "La cadena esta vacia"
fi

# Verificar si no esta vacia
if [ -n "$str" ]; then
    echo "La cadena no esta vacia"
fi

# Coincidencia de patrones usando [[ ]]
if [[ "$str" == h* ]]; then
    echo "Comienza con h"
fi

# Coincidencia de expresiones regulares (Bash 3.0 o posterior)
if [[ "$str" =~ ^[a-z]+$ ]]; then
    echo "Solo letras minusculas"
fi

Verificacion de archivos y directorios

#!/bin/bash

# Verificar existencia de archivo
if [ -f "config.txt" ]; then
    echo "config.txt existe"
fi

# Verificar existencia de directorio
if [ -d "logs" ]; then
    echo "El directorio logs existe"
fi

# Existe archivo o directorio
if [ -e "path" ]; then
    echo "path existe"
fi

# Legible
if [ -r "file.txt" ]; then
    echo "Es legible"
fi

# Escribible
if [ -w "file.txt" ]; then
    echo "Es escribible"
fi

# Ejecutable
if [ -x "script.sh" ]; then
    echo "Es ejecutable"
fi

# El archivo no esta vacio
if [ -s "file.txt" ]; then
    echo "El archivo no esta vacio"
fi

Operadores logicos

# AND
if [ $a -gt 0 ] && [ $a -lt 10 ]; then
    echo "0 < a < 10"
fi

# OR
if [ $a -eq 0 ] || [ $a -eq 1 ]; then
    echo "a is 0 or 1"
fi

# NOT
if [ ! -f "file.txt" ]; then
    echo "file.txt does not exist"
fi

# En [[ ]] puedes usar && y ||
if [[ $a -gt 0 && $a -lt 10 ]]; then
    echo "0 < a < 10"
fi

Sentencia case

La sentencia case es conveniente para multiples condiciones:

#!/bin/bash

fruit="apple"

case $fruit in
    apple)
        echo "Es una manzana"
        ;;
    banana|orange)
        echo "Es un platano o naranja"
        ;;
    *)
        echo "Fruta desconocida"
        ;;
esac

Bucles

Bucle for

#!/bin/bash

# Procesar lista en orden
for fruit in apple banana orange; do
    echo "Fruta: $fruit"
done

# Rango numerico (extension de Bash)
for i in {1..5}; do
    echo "Conteo: $i"
done

# Especificar paso
for i in {0..10..2}; do
    echo "Par: $i"
done

# Estilo C del for
for ((i=0; i<5; i++)); do
    echo "Index: $i"
done

# Procesar archivos
for file in *.txt; do
    echo "Archivo: $file"
done

# Procesar salida de comando
for user in $(cat users.txt); do
    echo "Usuario: $user"
done

Bucle while

#!/bin/bash

count=1
while [ $count -le 5 ]; do
    echo "Conteo: $count"
    count=$((count + 1))
done

# Leer archivo linea por linea (patron recomendado)
while IFS= read -r line; do
    echo "Linea: $line"
done < input.txt

# Bucle infinito
while true; do
    echo "Press Ctrl+C to stop"
    sleep 1
done

Bucle until

#!/bin/bash

count=1
until [ $count -gt 5 ]; do
    echo "Conteo: $count"
    count=$((count + 1))
done

Control de bucles

# Salir del bucle con break
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        break
    fi
    echo $i
done

# Saltar a la siguiente iteracion con continue
for i in {1..5}; do
    if [ $i -eq 3 ]; then
        continue
    fi
    echo $i
done

Definicion de funciones

#!/bin/bash

# Definicion de funcion (dos formas)
function greet() {
    local name=$1
    echo "Hola, ${name}!"
}

# O
greet2() {
    echo "Hello, $1!"
}

# Llamar a la funcion
greet "Taro"
greet2 "World"

# Valor de retorno (estado de salida)
is_even() {
    if (( $1 % 2 == 0 )); then
        return 0  # Exito (par)
    else
        return 1  # Fallo (impar)
    fi
}

if is_even 4; then
    echo "4 es par"
fi

# Usar echo para retornar valores
add() {
    echo $(( $1 + $2 ))
}

result=$(add 3 5)
echo "3 + 5 = $result"

Practica: Script de backup

Crearemos un script de backup practico usando lo que hemos aprendido.

backup.sh

#!/bin/bash
#
# Script de backup automatico
# Uso: ./backup.sh [source_dir] [backup_dir]
#

set -euo pipefail  # Terminar inmediatamente en caso de error

# Configuracion
SOURCE_DIR="${1:-./src}"
BACKUP_DIR="${2:-./backups}"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="backup_$DATE.tar.gz"
RETENTION_DAYS=7

# Funcion de log
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# Verificar/crear directorio de backup
if [ ! -d "$BACKUP_DIR" ]; then
    log "Creando directorio de backup..."
    mkdir -p "$BACKUP_DIR"
fi

# Verificar directorio fuente
if [ ! -d "$SOURCE_DIR" ]; then
    log "Error: $SOURCE_DIR no encontrado"
    exit 1
fi

# Ejecutar backup
log "Iniciando backup: $SOURCE_DIR -> $BACKUP_DIR/$BACKUP_NAME"
tar -czf "$BACKUP_DIR/$BACKUP_NAME" "$SOURCE_DIR"

if [ $? -eq 0 ]; then
    log "Completado: $BACKUP_DIR/$BACKUP_NAME"
    log "Tamano: $(du -h "$BACKUP_DIR/$BACKUP_NAME" | cut -f1)"
else
    log "Error: El backup ha fallado"
    exit 1
fi

# Eliminar backups antiguos
log "Eliminando backups antiguos (mas de ${RETENTION_DAYS} dias)..."
deleted=$(find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +$RETENTION_DAYS -delete -print | wc -l)
log "Archivos eliminados: $deleted"

log "¡Todo completado!"

Mejores practicas para scripts

  1. Usar set -euo pipefail

    • -e: Terminar inmediatamente en caso de error
    • -u: Tratar variables no definidas como error
    • -o pipefail: Detectar errores dentro de pipes
  2. Siempre encerrar variables entre comillas: "$variable"

  3. Usar funciones: Mejora la reutilizacion y legibilidad

  4. Incluir logs: Para depuracion y monitoreo

Manejo de errores

#!/bin/bash

# Definir procesamiento en caso de error con trap
cleanup() {
    echo "Ejecutando limpieza..."
    # Eliminar archivos temporales, etc.
}

trap cleanup EXIT  # Ejecutar al terminar el script
trap 'echo "Ocurrio un error: linea $LINENO"' ERR

# Si quieres ignorar errores
command_that_might_fail || true

# Procesamiento alternativo en caso de error
config_file="config.txt"
if [ -f "$config_file" ]; then
    source "$config_file"
else
    echo "Archivo de configuracion no encontrado. Usando valores por defecto."
    default_value="fallback"
fi

Consejos de depuracion

# Ejecutar script completo en modo debug
bash -x script.sh

# Habilitar debug dentro del script
set -x  # Iniciar debug
# ... codigo a depurar ...
set +x  # Terminar debug

# Terminar inmediatamente en caso de error (recomendado)
set -e

# Tratar variables no definidas como error (recomendado)
set -u

# Detectar errores en pipes (recomendado)
set -o pipefail

# Combinacion comun
set -euo pipefail

Analisis estatico con ShellCheck

ShellCheck es una herramienta de analisis estatico que detecta problemas en scripts de shell.

# Instalacion
# macOS
brew install shellcheck

# Ubuntu/Debian
sudo apt install shellcheck

# Uso
shellcheck script.sh

Ejemplos de problemas que ShellCheck detecta:

# Advertencia: falta de comillas en variable
echo $USER  # SC2086: Double quote to prevent globbing

# Corregido
echo "$USER"

El uso de ShellCheck tambien se recomienda en Google Shell Style Guide.

Patrones comunes

Validacion de argumentos

#!/bin/bash

if [ $# -lt 2 ]; then
    echo "Uso: $0 <source> <destination>"
    exit 1
fi

source=$1
destination=$2

Configurar valores por defecto

# Usar valor por defecto si la variable no esta definida o esta vacia
name="${1:-World}"
echo "Hello, $name"

# Usar valor por defecto solo si la variable no esta definida
name="${1-World}"

Creacion segura de archivos temporales

#!/bin/bash

# Usar mktemp (recomendado)
temp_file=$(mktemp)
temp_dir=$(mktemp -d)

# Eliminar al salir
trap "rm -rf $temp_file $temp_dir" EXIT

echo "Archivo temporal: $temp_file"
echo "Directorio temporal: $temp_dir"

Obtener entrada del usuario

#!/bin/bash

# Leer entrada
read -p "Ingresa tu nombre: " name
echo "Hola, $name"

# Entrada de contrasena (oculta)
read -sp "Contrasena: " password
echo ""

# Con timeout
read -t 5 -p "Ingresa en 5 segundos: " input || echo "Tiempo agotado"

Siguientes pasos

Una vez que domines los fundamentos de scripts de shell, avanza a los siguientes pasos:

  • Expresiones regulares y grep, sed, awk
  • Ejecucion programada con cron jobs
  • Automatizacion mas avanzada (Ansible, Makefile, etc.)

Enlaces de referencia

Documentacion oficial

Guias de estilo y mejores practicas

Recursos de aprendizaje

Hojas de referencia

← Volver a la lista