Expansión y Sustitución

La expansión es cuando Bash transforma algo que escribes en otra cosa antes de ejecutar el comando. Es el corazón del poder de Bash.
La Familia del $ - Mapa Completo
| Sintaxis | Nombre | Qué hace | Ejemplo |
|---|---|---|---|
$VAR |
Variable | Lee valor de variable | echo $HOME |
${VAR} |
Variable (segura) | Lee valor, permite modificadores | echo ${HOME} |
$(cmd) |
Sustitución de comando | Ejecuta cmd, devuelve su output | echo $(date) |
$((expr)) |
Expansión aritmética | Calcula matemáticas | echo $((2+2)) |
${VAR:-val} |
Default | Valor si VAR vacía | ${nombre:-Anónimo} |
${#VAR} |
Longitud | Cuenta caracteres | ${#nombre} |
1. Sustitución de Comandos: $(comando)
Ejecuta un comando y usa su salida como texto:
# Sin sustitución
echo date
# date (literal)
# Con sustitución
echo $(date)
# Mon Jan 27 10:30:00 CST 2026 (ejecuta date)
Usos prácticos
# Guardar resultado en variable
fecha_actual=$(date)
usuario_actual=$(whoami)
directorio=$(pwd)
echo "Usuario: $usuario_actual"
echo "En: $directorio"
echo "Fecha: $fecha_actual"
# Usar directamente en texto
echo "Hoy es $(date +%A)"
echo "Hay $(ls | wc -l) archivos aquí"
echo "Tu IP es $(hostname -I | cut -d' ' -f1)"
# Crear nombres de archivo dinámicos
archivo="backup_$(date +%Y%m%d).tar.gz"
echo $archivo
# backup_20260127.tar.gz
# Crear directorios con fecha
mkdir "logs_$(date +%Y%m%d)"
# 1. Guarda comandos en variables
mi_usuario=$(whoami)
mi_shell=$(echo $SHELL | cut -d'/' -f3)
num_archivos=$(ls ~ | wc -l)
# 2. Usa las variables
echo "Soy $mi_usuario, uso $mi_shell"
echo "Tengo $num_archivos archivos en mi home"
# 3. Crea un nombre de archivo único
archivo_log="log_${mi_usuario}_$(date +%H%M%S).txt"
echo "Archivo: $archivo_log"
Backticks (forma antigua)
También puedes usar backticks, pero $() es preferido:
# Forma moderna (preferida)
echo $(date)
# Forma antigua (funciona pero evítala)
echo `date`
¿Por qué preferir $()?
- Se puede anidar:
$(cmd1 $(cmd2)) - Más fácil de leer
- Menos errores con comillas
2. Expansión Aritmética: $((expresión))
Bash puede hacer matemáticas:
echo $((5 + 3)) # 8
echo $((10 - 4)) # 6
echo $((3 * 7)) # 21
echo $((20 / 4)) # 5
echo $((17 % 5)) # 2 (residuo/módulo)
echo $((2 ** 8)) # 256 (potencia)
Con variables
a=10
b=3
echo $((a + b)) # 13
echo $((a * b)) # 30
echo $((a / b)) # 3 (división entera)
echo $((a % b)) # 1
# Incrementar
a=$((a + 1))
echo $a # 11
Operadores disponibles
| Operador | Descripción | Ejemplo |
|---|---|---|
+ |
Suma | $((5+3)) → 8 |
- |
Resta | $((5-3)) → 2 |
* |
Multiplicación | $((5*3)) → 15 |
/ |
División (entera) | $((5/3)) → 1 |
% |
Módulo (residuo) | $((5%3)) → 2 |
** |
Potencia | $((2**3)) → 8 |
# Datos
precio=100
cantidad=5
impuesto=16
# Cálculos
subtotal=$((precio * cantidad))
iva=$((subtotal * impuesto / 100))
total=$((subtotal + iva))
# Mostrar
echo "Subtotal: \$$subtotal"
echo "IVA ($impuesto%): \$$iva"
echo "Total: \$$total"
3. Expansión de Variables Avanzada: ${VAR...}
Además de leer variables, ${} permite modificarlas al vuelo.
${VAR:-default} - Valor por defecto
Si la variable está vacía o no existe, usa el valor default:
# Variable existe
nombre="Juan"
echo ${nombre:-Anónimo}
# Juan
# Variable vacía
nombre=""
echo ${nombre:-Anónimo}
# Anónimo
# Variable no existe
unset nombre
echo ${nombre:-Anónimo}
# Anónimo
Uso práctico:
# Usar variable de entorno o valor por defecto
editor=${EDITOR:-nano}
echo "Usando editor: $editor"
# Puerto con default
puerto=${PUERTO:-8080}
echo "Servidor en puerto $puerto"
${VAR:=default} - Asignar si vacía
Similar al anterior, pero también asigna el valor:
unset mi_var
echo ${mi_var:=valor_default}
# valor_default
echo $mi_var
# valor_default (ahora está asignada)
${#VAR} - Longitud de la variable
nombre="Francisco"
echo ${#nombre}
# 9
password="secreto123"
echo "Tu contraseña tiene ${#password} caracteres"
# Tu contraseña tiene 10 caracteres
${VAR:inicio:longitud} - Subcadena
texto="Hola Mundo"
echo ${texto:0:4} # Hola (desde 0, 4 caracteres)
echo ${texto:5:5} # Mundo (desde 5, 5 caracteres)
echo ${texto:5} # Mundo (desde 5 hasta el final)
${VAR/patron/reemplazo} - Reemplazar
archivo="documento.txt"
# Reemplazar primera ocurrencia
echo ${archivo/.txt/.pdf}
# documento.pdf
# Reemplazar todas las ocurrencias (doble /)
texto="uno dos uno tres uno"
echo ${texto//uno/UNO}
# UNO dos UNO tres UNO
# 1. Valores por defecto
nombre=${NOMBRE:-Usuario}
echo "Hola, $nombre"
# 2. Longitud
email="usuario@ejemplo.com"
echo "Tu email tiene ${#email} caracteres"
# 3. Subcadenas
fecha="2026-01-27"
año=${fecha:0:4}
mes=${fecha:5:2}
dia=${fecha:8:2}
echo "Año: $año, Mes: $mes, Día: $dia"
# 4. Reemplazo
archivo="foto_vacaciones.jpg"
echo "Original: $archivo"
echo "Thumbnail: ${archivo/.jpg/_thumb.jpg}"
4. Expansión de Llaves: {...}
Genera múltiples strings a partir de un patrón:
Lista de valores
echo {a,b,c}
# a b c
echo archivo_{uno,dos,tres}.txt
# archivo_uno.txt archivo_dos.txt archivo_tres.txt
Secuencias
echo {1..5}
# 1 2 3 4 5
echo {a..e}
# a b c d e
echo {01..10}
# 01 02 03 04 05 06 07 08 09 10
echo archivo{1..3}.txt
# archivo1.txt archivo2.txt archivo3.txt
Usos prácticos
# Crear múltiples directorios
mkdir proyecto/{src,tests,docs,data}
# Crear archivos numerados
touch log_{01..05}.txt
# Backup con fecha
cp config.txt config.txt.{bak,$(date +%Y%m%d)}
# 1. Lista
echo {perro,gato,pez}
# 2. Secuencia numérica
echo {1..10}
# 3. Secuencia con padding
echo {01..10}
# 4. Crear estructura de proyecto
mkdir -p mi_proyecto/{src,tests,docs}
ls mi_proyecto/
# 5. Múltiples extensiones
touch archivo.{txt,md,py}
ls archivo.*
Combinando Todo
El verdadero poder viene de combinar tipos de expansión:
# Variables + sustitución de comando
usuario=$(whoami)
fecha=$(date +%Y%m%d)
backup_dir="/backups/${usuario}/${fecha}"
echo $backup_dir
# /backups/tu_usuario/20260127
# Aritmética + variables
archivos=$(ls | wc -l)
echo "Tienes $archivos archivos, el doble sería $((archivos * 2))"
# Default + sustitución
LOG_DIR=${LOG_DIR:-/var/log}
ultimo_log=$(ls -t $LOG_DIR/*.log 2>/dev/null | head -1)
echo "Último log: ${ultimo_log:-No hay logs}"
# Todo junto
proyecto=${1:-mi_proyecto}
mkdir -p "$proyecto"/{src,tests,docs}
echo "# $proyecto" > "$proyecto/README.md"
echo "Creado el $(date)" >> "$proyecto/README.md"
echo "Proyecto $proyecto creado"
# Crear estructura de proyecto con fecha
nombre_proyecto="app"
fecha=$(date +%Y%m%d_%H%M%S)
dir_proyecto="${nombre_proyecto}_${fecha}"
# Crear estructura
mkdir -p "$dir_proyecto"/{src,tests,docs,data/{raw,processed}}
# Crear README
cat << EOF > "$dir_proyecto/README.md"
# $nombre_proyecto
Creado: $(date)
Usuario: $(whoami)
Directorio: $(pwd)/$dir_proyecto
## Estructura
- src/ - código fuente
- tests/ - pruebas
- docs/ - documentación
- data/ - datos
EOF
# Verificar
echo "Proyecto creado:"
ls -R "$dir_proyecto"
echo ""
echo "README:"
cat "$dir_proyecto/README.md"
Errores Comunes
Confundir $() con $(())
# $() ejecuta un comando
echo $(pwd) # /home/usuario
# $(()) hace matemáticas
echo $((2 + 2)) # 4
# ERROR común
echo $((pwd)) # Error - pwd no es una expresión matemática
echo $(2 + 2) # Error - "2" no es un comando
Olvidar comillas con espacios
archivo="mi archivo.txt"
# MAL - se interpreta como dos argumentos
ls $archivo
# ls: cannot access 'mi': No such file
# BIEN - comillas preservan el espacio
ls "$archivo"
Tabla Resumen
| Sintaxis | Nombre | Ejemplo | Resultado |
|---|---|---|---|
$VAR |
Variable | echo $HOME |
/home/user |
${VAR} |
Variable segura | echo ${USER} |
user |
$(cmd) |
Sustitución comando | $(date +%Y) |
2026 |
$((expr)) |
Aritmética | $((5*3)) |
15 |
${VAR:-def} |
Default | ${X:-none} |
none |
${#VAR} |
Longitud | ${#HOME} |
10 |
${VAR:0:4} |
Subcadena | ${HOME:0:4} |
/hom |
{a,b,c} |
Lista | echo {1,2,3} |
1 2 3 |
{1..5} |
Secuencia | echo {1..5} |
1 2 3 4 5 |
Siguiente: Ahora sí - vamos a poner todo junto en scripts de Bash.