GNU Bash

Curso de programación con Bash

Bash es el shell y lenguaje de scripting para sistemas Unix. Aprende a escribir scripts y automatizar tareas en tu entorno Unix/Linux con Bash.

Aprende Bash GRATIS y certifícate

Bash (Bourne Again SHell) es un intérprete de comandos y lenguaje de scripting que actúa como interfaz entre el usuario y el sistema operativo Unix/Linux. Desarrollada como parte del Proyecto GNU, es una evolución mejorada de la shell Bourne original (sh) que incorpora características de otras shells como Korn shell (ksh) y C shell (csh).

Como shell predeterminada en la mayoría de distribuciones Linux, macOS (hasta Catalina) y disponible en Windows a través de WSL (Windows Subsystem for Linux), Bash se ha convertido en una herramienta esencial para administradores de sistemas, desarrolladores y usuarios avanzados.

Cuando abres una terminal, generalmente interactúas con Bash a través de un prompt que muestra información como tu nombre de usuario, el host y el directorio actual:

usuario@host:~/documentos$

El símbolo $ indica un usuario regular, mientras que # aparece cuando trabajas como superusuario (root).

Para verificar qué shell estás utilizando:

echo $SHELL

Para conocer la versión de Bash:

bash --version

Sintaxis básica

La estructura general de un comando en Bash es:

comando [opciones] [argumentos]

Por ejemplo:

ls -la /home/usuario

Donde:

  • ls es el comando que lista archivos
  • -la son las opciones (l: formato largo, a: mostrar archivos ocultos)
  • /home/usuario es el argumento (el directorio a listar)

Puedes ejecutar varios comandos en una misma línea utilizando diferentes separadores:

Punto y coma (;): Ejecuta los comandos secuencialmente independientemente del resultado.

echo "Primero"; echo "Segundo"

AND lógico (&&): El segundo comando se ejecuta solo si el primero tiene éxito.

mkdir directorio && cd directorio

OR lógico (||): El segundo comando se ejecuta solo si el primero falla.

grep "patrón" archivo.txt || echo "Patrón no encontrado"

Los comentarios en Bash comienzan con el símbolo #:

# Este es un comentario
echo "Hola mundo" # Este es un comentario al final de una línea

Variables

En Bash, las variables se declaran sin especificar tipo:

nombre="Ana"
edad=25
es_estudiante=true

Reglas importantes:

  • No debe haber espacios antes ni después del signo igual (=)
  • Los nombres de variables pueden contener letras, números y guiones bajos, pero no pueden comenzar con un número
  • Las variables son sensibles a mayúsculas y minúsculas

Para acceder al valor de una variable, se usa el prefijo $:

echo "Hola, $nombre. Tienes $edad años."

También puedes usar la forma ${variable}, que es útil para desambiguar:

echo "Hola, ${nombre}s" # Para mostrar "Hola, Anas"

Bash proporciona varias variables especiales:

  • $0: Nombre del script
  • $1, $2, etc.: Argumentos posicionales
  • $#: Número de argumentos
  • $@: Todos los argumentos como palabras separadas
  • $*: Todos los argumentos como una sola palabra
  • $?: Código de salida del último comando (0 indica éxito)
  • $$: PID (Process ID) de la shell actual
  • $!: PID del último proceso en segundo plano

Las variables de entorno afectan el comportamiento del sistema y los programas:

echo $HOME      # Directorio principal del usuario
echo $PATH      # Rutas donde se buscan ejecutables
echo $USER      # Nombre de usuario actual

Para crear una variable de entorno que esté disponible para los procesos hijo:

export MI_VARIABLE="valor"

Arrays (matrices)

Bash soporta arrays indexados (desde 0) y, en versiones más recientes, arrays asociativos.

Arrays indexados:

# Declaración e inicialización
frutas=("manzana" "naranja" "plátano")

# Acceso a elementos
echo ${frutas[0]}     # manzana

# Añadir elemento
frutas+=("uva")

# Mostrar todos los elementos
echo ${frutas[@]}

# Número de elementos
echo ${#frutas[@]}

Arrays asociativos (desde Bash 4.0):

# Declaración (requerida para arrays asociativos)
declare -A colores

# Asignación
colores[rojo]="#FF0000"
colores[verde]="#00FF00"
colores[azul]="#0000FF"

# Acceso
echo ${colores[rojo]}

# Todas las claves
echo ${!colores[@]}

Operadores

Para realizar operaciones aritméticas, Bash ofrece varias sintaxis:

# La forma recomendada
resultado=$((5 + 3))

Operadores disponibles:

  • +: Suma
  • -: Resta
  • *: Multiplicación
  • /: División (entera)
  • %: Módulo (resto)
  • **: Exponenciación
echo $((10 + 5))    # 15
echo $((10 - 5))    # 5
echo $((10 * 5))    # 50
echo $((10 / 3))    # 3 (división entera)
echo $((10 % 3))    # 1 (resto)
echo $((2 ** 3))    # 8 (potencia)

Para incremento y decremento:

((variable++))     # Incremento después de usar
((++variable))     # Incremento antes de usar

Operadores de comparación numérica (dentro de (( ))):

if ((a < b)); then
    echo "a es menor que b"
fi
  • ==: Igual
  • !=: No igual
  • <: Menor que
  • <=: Menor o igual
  • >: Mayor que
  • >=: Mayor o igual

Operadores de comparación de cadenas (dentro de [[ ]]):

if [[ "$a" == "$b" ]]; then
    echo "Las cadenas son iguales"
fi
  • ==: Igual
  • !=: No igual
  • <: Menor en orden lexicográfico
  • >: Mayor en orden lexicográfico
  • -z: Verdadero si la cadena está vacía
  • -n: Verdadero si la cadena no está vacía

Operadores para comprobación de archivos:

if [[ -f "$archivo" ]]; then
    echo "Es un archivo regular"
fi
  • -f: Es un archivo regular
  • -d: Es un directorio
  • -e: Existe (archivo o directorio)
  • -r: Tiene permiso de lectura
  • -w: Tiene permiso de escritura
  • -x: Tiene permiso de ejecución
  • -s: Tamaño mayor que cero
  • -L: Es un enlace simbólico

Estructuras de control

La estructura if:

if [[ condición ]]; then
    # comandos
elif [[ otra_condición ]]; then
    # otros comandos
else
    # comandos por defecto
fi

Nota: Los espacios dentro de [[ ]] son importantes.

Estructura case:

case $variable in
    patrón1)
        # comandos
        ;;
    patrón2|patrón3)
        # comandos para múltiples patrones
        ;;
    *)
        # caso por defecto
        ;;
esac

Bucle for (varios formatos):

# Iteración sobre lista
for nombre in Juan Ana Pedro; do
    echo "Hola, $nombre"
done

# Rango numérico
for i in {1..5}; do
    echo "Número: $i"
done

# Sintaxis estilo C
for ((i=0; i<5; i++)); do
    echo "Iteración $i"
done

# Iteración sobre archivos
for archivo in *.txt; do
    echo "Procesando $archivo"
done

Bucle while:

contador=0
while [[ $contador -lt 5 ]]; do
    echo "Contador: $contador"
    ((contador++))
done

Bucle until (continúa hasta que la condición sea verdadera):

contador=0
until [[ $contador -ge 5 ]]; do
    echo "Contador: $contador"
    ((contador++))
done

Control de bucles:

for i in {1..10}; do
    if [[ $i -eq 5 ]]; then
        continue    # Salta a la siguiente iteración
    fi
    
    if [[ $i -eq 8 ]]; then
        break       # Sale del bucle
    fi
    
    echo "Número: $i"
done

Funciones

Definición y llamada de funciones:

# Definición con la palabra clave "function"
function saludar {
    echo "Hola, $1!"
}

# Forma alternativa
saludar_alternativa() {
    echo "Hola, $1!"
}

# Llamada
saludar "Juan"

Las funciones en Bash no devuelven valores como en otros lenguajes, pero existen varias técnicas:

# Usando código de salida
function es_par {
    if (( $1 % 2 == 0 )); then
        return 0    # verdadero en Bash
    else
        return 1    # falso en Bash
    fi
}

# Verificación
if es_par 4; then
    echo "Es par"
fi

# Usando echo y captura
function suma {
    echo $(( $1 + $2 ))
}

resultado=$(suma 5 3)
echo "La suma es: $resultado"

Las variables en Bash son globales por defecto, a menos que se declaren como local:

variable_global="Global"

function prueba_alcance {
    local variable_local="Local"
    echo "Dentro: global=$variable_global, local=$variable_local"
}

prueba_alcance
echo "Fuera: global=$variable_global"
echo "Intentando acceder a variable local: $variable_local" # Será vacío

Entrada y salida

Para obtener entrada del usuario:

# Lectura básica
echo "Introduce tu nombre:"
read nombre
echo "Hola, $nombre"

# Con prompt incorporado
read -p "Introduce tu edad: " edad
echo "Tienes $edad años"

# Entrada silenciosa (para contraseñas)
read -sp "Contraseña: " password
echo -e "\nContraseña recibida"

Redirección de entrada/salida:

# Redirigir salida estándar a archivo (sobrescribir)
comando > archivo.txt

# Redirigir y anexar salida estándar a archivo
comando >> archivo.txt

# Redirigir error estándar
comando 2> errores.log

# Redirigir ambos (salida y error) a archivos diferentes
comando > salida.txt 2> errores.log

# Redirigir ambos al mismo archivo
comando > archivo.txt 2>&1

# Forma moderna para redirigir ambos
comando &> archivo.txt

# Descartar salida
comando > /dev/null

Las tuberías (pipes) permiten conectar la salida de un comando con la entrada de otro:

# Filtrar resultados
ls -la | grep ".txt"

# Encadenar múltiples comandos
cat archivo.txt | tr '[:lower:]' '[:upper:]' | sort > resultado.txt

# Contar líneas/palabras/caracteres
cat archivo.txt | wc -l

Procesamiento de archivos

Lectura de archivos:

# Línea por línea
while IFS= read -r linea; do
    echo "Leído: $linea"
done < archivo.txt

# Todo el archivo de una vez
contenido=$(<archivo.txt)
echo "$contenido"

# Usando IFS para CSV
while IFS=, read -r campo1 campo2 campo3; do
    echo "Campo 1: $campo1, Campo 2: $campo2, Campo 3: $campo3"
done < datos.csv

Escritura en archivos:

# Sobrescribir
echo "Nuevo contenido" > archivo.txt

# Añadir
echo "Contenido adicional" >> archivo.txt

# Usando redirección heredoc
cat << EOF > archivo.txt
Línea 1
Línea 2
Línea con variable: $variable
EOF

# Here-doc con variables no expandidas
cat << 'EOF' > archivo.txt
Línea con $variable sin expandir
EOF

Expansiones y sustituciones

Expansión de variables:

nombre="Juan"
echo "${nombre}"            # Expansión simple
echo "${nombre}s"           # Desambiguación
echo "${nombre:-defecto}"   # Valor por defecto si no existe o está vacía
echo "${nombre:=defecto}"   # Asignar valor por defecto si no existe o está vacía
echo "${nombre:+alternativo}" # Valor alternativo si existe y no está vacía
echo "${nombre:?mensaje}"   # Error con mensaje si no existe o está vacía

Manipulación de cadenas:

cadena="Hola Mundo"
echo "${cadena:5}"       # "Mundo" (a partir del índice 5)
echo "${cadena:0:4}"     # "Hola" (4 caracteres desde el índice 0)
echo "${#cadena}"        # 10 (longitud de la cadena)

# Manipulación de patrones
archivo="imagen.jpg"
echo "${archivo%.jpg}"   # "imagen" (elimina .jpg del final)
echo "${archivo#imagen}" # ".jpg" (elimina imagen del principio)

# Sustitución
echo "${cadena/Mundo/Amigo}" # "Hola Amigo" (reemplaza primera coincidencia)
echo "${cadena//o/O}"       # "HOla MundO" (reemplaza todas las coincidencias)

# Transformaciones (Bash 4.0+)
echo "${cadena^^}"       # "HOLA MUNDO" (todo a mayúsculas)
echo "${cadena,,}"       # "hola mundo" (todo a minúsculas)

Expansión de comandos:

# Sintaxis moderna (recomendada)
fecha_actual=$(date)
echo "La fecha actual es: $fecha_actual"

# Anidamiento
echo "Archivos encontrados: $(find . -name "*.txt" | wc -l)"

# Cálculo aritmético
echo "Resultado: $((5 * $(wc -l < archivo.txt)))"

Expansión de llaves:

# Generación de secuencias
echo {1..5}        # "1 2 3 4 5"
echo {a..e}        # "a b c d e"
echo {1..10..2}    # "1 3 5 7 9" (paso de 2)

# Combinaciones
echo file{1,2,3}.txt  # "file1.txt file2.txt file3.txt"
echo archivo.{jpg,png,gif}    # "archivo.jpg archivo.png archivo.gif"

# Anidamiento
echo {a,b}{1,2}      # "a1 a2 b1 b2"

Control de procesos

Ejecución en segundo plano:

# Ejecutar comando en segundo plano
comando &

# Verificar trabajos en ejecución
jobs

# Traer trabajo a primer plano
fg %1

# Enviar trabajo a segundo plano (después de Ctrl+Z)
bg %1

Gestión de procesos:

# Listar procesos
ps aux
ps -ef

# Filtrar procesos
ps aux | grep "firefox"

# Matar proceso por PID
kill 1234

# Forzar terminación
kill -9 1234

# Matar proceso por nombre
pkill firefox
killall firefox

Ejecución condicional y señales:

# Capturar señal Ctrl+C (SIGINT)
trap "echo 'Operación cancelada'; exit" SIGINT

# Limpiar al salir
trap "echo 'Limpiando...'; rm -f /tmp/archivo_temp.$$" EXIT

# Ignorar señal
trap "" SIGTERM

# Restablecer comportamiento predeterminado
trap - SIGINT

Scripting avanzado

Estructura de un script:

#!/bin/bash
# Descripción: Este script hace X
# Autor: Tu nombre
# Fecha: YYYY-MM-DD

# Configuración
set -euo pipefail

# Constantes
readonly CONFIG_FILE="/etc/miapp.conf"
readonly MAX_INTENTOS=3

# Funciones
function log_mensaje() {
    local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
    echo "[$timestamp] $1"
}

# Código principal
log_mensaje "Iniciando script"

# ... resto del script

log_mensaje "Script completado"
exit 0

Opciones de línea de comandos:

while getopts ":f:v:h" opt; do
  case $opt in
    f)
      archivo="$OPTARG"
      ;;
    v)
      nivel_verbosidad="$OPTARG"
      ;;
    h)
      echo "Uso: $0 [-f archivo] [-v nivel] [-h]"
      exit 0
      ;;
    \?)
      echo "Opción inválida: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "La opción -$OPTARG requiere un argumento." >&2
      exit 1
      ;;
  esac
done

Manejo de errores:

# Salir al primer error
set -e

# Detectar uso de variables no definidas
set -u

# Propagar errores en tuberías
set -o pipefail

# Todo junto (recomendado para scripts robustos)
set -euo pipefail

# Función de error
function error_salida() {
    local linea=$1
    local mensaje=$2
    echo "Error en línea $linea: $mensaje" >&2
    exit 1
}

# Usar trap para capturar errores con información de línea
trap 'error_salida ${LINENO} "$BASH_COMMAND"' ERR

Inclusión de archivos:

# Cargar funciones o variables de otro archivo
source funciones.sh
# o
. funciones.sh

# Verificar si el archivo existe antes de incluirlo
if [[ -f config.sh ]]; then
    source config.sh
else
    echo "Archivo de configuración no encontrado" >&2
    exit 1
fi

Buenas prácticas

Seguridad:

# Comillas en variables para evitar problemas con espacios
echo "Procesando archivo: \"$archivo\""

# Validar entrada antes de usar
if [[ ! $numero =~ ^[0-9]+$ ]]; then
    echo "Error: '$numero' no es un número válido" >&2
    exit 1
fi

# Evitar inyección de comandos
archivo_seguro=$(printf '%q' "$nombre_archivo")
eval "cp $archivo_seguro /destino/"

Portabilidad:

# Usar shebang con env para mayor portabilidad
#!/usr/bin/env bash

# Comprobar disponibilidad de comandos
if ! command -v jq &> /dev/null; then
    echo "Error: jq no está instalado" >&2
    exit 1
fi

# Establecer ruta predeterminada para comandos
PATH="/usr/local/bin:/usr/bin:/bin"

Eficiencia:

# Evitar llamadas externas innecesarias
if [[ "$cadena" == *"subcadena"* ]]; then  # Expansión de patrón nativa
    echo "Contiene subcadena"
fi

# Uso de parámetros nativos
echo "${#cadena}"  # en lugar de echo "$cadena" | wc -c

# Usar mapeos internos para transformaciones simples
echo "${cadena^^}" # en lugar de echo "$cadena" | tr '[:lower:]' '[:upper:]'

Depuración:

# Mostrar comandos al ejecutarse
set -x
comando1
comando2
set +x

# Solo activar depuración si se establece una variable
if [[ "${DEBUG:-}" == "true" ]]; then
    set -x
fi

# Ejecutar script con depuración
bash -x script.sh
Aprende Bash GRATIS online

Tutoriales de Bash

Aprende Bash con tutoriales de programación en Bash.

Ejercicios de programación de Bash

Evalúa tus conocimientos en Bash con ejercicios de programación Bash de tipo Test, Puzzle, Código y Proyecto con VSCode.

Tipo de tecnología

Lenguaje

Categoría laboral

Administración de sistemas

Año de lanzamiento

1989

Developers

Chet Ramey

Todos los módulos del curso de Bash

Ver todos los módulos de Bash

Otras tecnologías

Rust
Rust

Rust

Backend

Lenguaje de programación de sistemas enfocado en seguridad, concurrencia y rendimiento.

C
C

C

Backend

Lenguaje de propósito general, eficiente y de bajo nivel.

R
R

R

Ciencia de Datos e IA

Lenguaje para análisis estadístico, manipulación de datos y visualización gráfica.

TypeScript
TypeScript

TypeScript

Full Stack

Superconjunto de JavaScript con tipado estático.

Apache Spark
PySpark

PySpark

Big Data

Motor unificado de análisis de datos distribuido para grandes volúmenes.

Go
Go

Go

Backend

Lenguaje de programación eficiente y concurrente creado por Google.

Django
Django

Django

Backend

Framework web Python para desarrollo rápido y seguro.

Spring Boot
SpringBoot

SpringBoot

Backend

Framework para desarrollo rápido de aplicaciones Java.

Laravel
Laravel

Laravel

Backend

Framework de PHP para desarrollo web backend.

Node.js
Node

Node

Backend

Node.js es un entorno de ejecución de JavaScript basado en el motor V8 de Google.

Java

Java

Backend

Lenguaje de programación versátil y multiplataforma.

OpenCV
OpenCV

OpenCV

Ciencia de Datos e IA

Biblioteca de Python para Computer Vision en imágenes y vídeos.

Flask
Flask

Flask

Backend

Microframework web para Python, simple y flexible.

NestJS
Nest

Nest

Backend

Framework Node.js para crear aplicaciones escalables y eficientes.

Selenium
Selenium

Selenium

Testing / QA

Suite de herramientas open-source para automatizar navegadores web y pruebas de software de interfaz de usuario.

React
React

React

Frontend

Librería framework para frontend interfaces de usuario.

Vue.js
Vuejs

Vuejs

Frontend

Framework de JS progresivo para construir interfaces de usuario reactivas y modulares.

Docker
Docker

Docker

DevOps

Plataforma de contenedores para aplicaciones portátiles.

PHP
PHP

PHP

Backend

Lenguaje de programación para desarrollo web del lado del servidor, el motor del ecosistema Wordpress.

CSharp

CSharp

Backend

Lenguaje C# de Microsoft para desarrollo en el ecosistema .NET para todo tipo de aplicaciones.

Streamlit
Streamlit

Streamlit

Ciencia de Datos e IA

Biblioteca Python para prototipado web UI rápido en ciencia de datos.

Seaborn

Seaborn

Ciencia de Datos e IA

Biblioteca de visualización de datos para Python.

SQL

SQL

Bases de datos

Lenguaje para gestionar bases de datos relacionales.

FastAPI
FastAPI

FastAPI

Backend

Framework web moderno y rápido para Python.

Fundamentos

Fundamentos

Full Stack

Fundamentos y bases de la programación de software moderna.

TensorFlow
TensorFlow

TensorFlow

Ciencia de Datos e IA

Biblioteca Python para redes neuronales en Deep Learning

Tailwind CSS
TailwindCSS

TailwindCSS

Frontend

Framework de utilidades CSS para diseños rápidos y personalizables.

Git
Git

Git

DevOps

Sistema de control de versiones distribuido.

scikit-learn
ScikitLearn

ScikitLearn

Ciencia de Datos e IA

Biblioteca de aprendizaje automático en Python.

Kotlin
Kotlin

Kotlin

Backend

Lenguaje de programación moderno y seguro para aplicaciones Android.

NumPy
Numpy

Numpy

Ciencia de Datos e IA

Biblioteca Python para computación científica y matrices.

HTML5
HTML

HTML

Frontend

Lenguaje de marcado para estructurar contenido web.

Bootstrap
Bootstrap

Bootstrap

Frontend

Framework CSS para diseños web responsive y modernos.

2022-09-27T22:26:51.030457 image/svg+xml Matplotlib v3.6.0, https://matplotlib.org/
Matplotlib

Matplotlib

Ciencia de Datos e IA

Biblioteca Python para crear gráficos y visualizaciones.

Hibernate
Hibernate

Hibernate

Backend

ORM para Java, simplifica el acceso a bases de datos.

pandas
Pandas

Pandas

Ciencia de Datos e IA

Herramienta Python para análisis y manipulación de datos.

JavaScript
JavaScript

JavaScript

Full Stack

Lenguaje de scripting para desarrollo web interactivo.

Angular
Angular

Angular

Frontend

Framework web de Google para aplicaciones dinámicas.

CSS
CSS

CSS

Frontend

Lenguaje de estilo para diseñar páginas web atractivas.

Python
Python

Python

Backend

Lenguaje de programación fácil de aprender y versátil.

Acceder a todas las tecnologías