Skip to content

variables-y-datos

Cómo almacenar y manipular información en Bash: variables, comillas, argumentos, entrada del usuario, aritmética, cadenas, arrays y expansiones.

Bash no tiene tipos estrictos: todo es texto salvo que se indique lo contrario.

Terminal window
nombre="mundo" # asignación: SIN espacios alrededor del =
echo "Hola, $nombre" # uso con $
echo "${nombre}s" # llaves para delimitar el nombre
unset nombre # eliminar

Reglas de nombres: letras, números y _; no pueden empezar por número; distingue mayúsculas de minúsculas.

Terminal window
declare -i numero=5 # entero
declare -r CONSTANTE=10 # solo lectura (equivale a readonly)
declare -x VAR=valor # exportada (equivale a export)
declare -a lista # array indexado
declare -A mapa # array asociativo
Terminal window
readonly MAX=3
MAX=4 # error: variable de solo lectura
Terminal window
fecha=$(date +%F)
usuarios=$(wc -l < /etc/passwd)
Terminal window
nombre = "x" # incorrecto: Bash lo interpreta como un comando "nombre"
nombre="x" # correcto
Tipo Efecto
'texto' Literal. No expande nada.
"texto" Expande variables ($var), comandos ($(...)) y aritmética; evita separar por espacios.
\c Escapa el carácter siguiente.
$'texto' Interpreta secuencias como \n, \t.
Terminal window
var="mundo"
echo 'Hola $var' # Hola $var
echo "Hola $var" # Hola mundo
echo "Precio: \$5" # Precio: $5
echo $'Linea1\nLinea2'
Terminal window
fichero="mi documento.txt"
rm $fichero # intenta borrar "mi" y "documento.txt"
rm "$fichero" # correcto

Regla práctica: entrecomillar siempre "$variable" salvo que se quiera explícitamente la separación por palabras o el globbing.

Terminal window
echo -e "a\tb\nc" # -e interpreta escapes
echo -n "sin salto" # -n evita el salto de línea
printf '%s tiene %d años\n' "Ana" 30 # más predecible y portable
#!/bin/bash
echo "Script: $0"
echo "Primero: $1"
echo "Segundo: $2"
echo "Total: $#"
echo "Todos: $@"
./script.sh
./script.sh uno dos
# Primero: uno
# Segundo: dos
# Total: 2

A partir del décimo argumento hacen falta llaves: ${10}.

Variable Contenido
$0 Nombre del script
$1 … $9, ${10} Argumentos posicionales
$# Número de argumentos
$@ Todos los argumentos (cada uno por separado si va entre comillas)
$* Todos los argumentos (como una sola cadena si va entre comillas)
$? Código de salida del último comando
$$ PID del proceso actual
$! PID del último proceso lanzado en segundo plano
Terminal window
for arg in "$@"; do echo "[$arg]"; done # respeta cada argumento, incluso con espacios

Casi siempre se usa "$@".

Descarta el primer argumento y desplaza los demás.

Terminal window
while [[ $# -gt 0 ]]; do
echo "Procesando $1"
shift
done
Terminal window
if [[ $# -lt 1 ]]; then
echo "Uso: $0 <fichero>" >&2
exit 1
fi
Terminal window
read -r nombre # lee una línea
read -rp "Nombre: " nombre # con mensaje
read -rsp "Clave: " clave; echo # sin eco (el echo final da el salto de línea)
read -t 5 -rp "Responde: " resp # tiempo límite de 5 s
read -n 1 -rp "¿Seguir? (s/n) " r # un solo carácter
Opción Efecto
-r No interpreta \ (recomendado siempre)
-p Muestra un mensaje
-s Silencioso (no muestra lo escrito)
-t n Tiempo máximo de espera
-n n Lee solo n caracteres
-a arr Guarda las palabras en un array

Sin nombre de variable, el valor queda en $REPLY.

Terminal window
linea="root:x:0:0"
IFS=: read -r usuario _ uid gid <<< "$linea"
echo "$usuario $uid" # root 0
Terminal window
read -rp "¿Continuar? (s/n) " r
[[ $r == [sS] ]] || exit 0

Bash opera solo con enteros.

Terminal window
a=7; b=2
echo $((a + b)) # 9
echo $((a / b)) # 3 (división entera)
echo $((a % b)) # 1 (resto)
echo $((a ** b)) # 49 (potencia)
Terminal window
resultado=$((a * b)) # expansión aritmética (la más usada)
(( a++ )) # evaluación sin devolver valor
(( a > b )) && echo "a es mayor"
let c=a+b # forma antigua
declare -i d=a+b # variable entera

Incremento: ((contador++)) o contador=$((contador+1)).

Con set -e, ((x++)) cuando x vale 0 devuelve código 1 y puede abortar el script. Es preferible x=$((x+1)).

Bash no los admite; se delega en otras herramientas:

Terminal window
echo "scale=2; 7/2" | bc # 3.50
awk 'BEGIN { printf "%.2f\n", 7/2 }' # 3.50
Terminal window
echo $RANDOM # 0 a 32767
echo $((RANDOM % 100)) # 0 a 99
seq 1 5 # 1 2 3 4 5

Permite manipular el valor de una variable sin comandos externos.

Terminal window
f="informe.final.txt"
Expresión Resultado Significado
${#f} 17 Longitud
${f:0:7} informe Desde posición 0, 7 caracteres
${f:8} final.txt Desde la posición 8 hasta el final
${f: -3} txt Últimos 3 caracteres (espacio antes del -)
Expresión Resultado Significado
${f#*.} final.txt Quita el prefijo más corto que coincida
${f##*.} txt Quita el prefijo más largo (extensión)
${f%.*} informe.final Quita el sufijo más corto
${f%%.*} informe Quita el sufijo más largo

Regla mnemotécnica: # actúa por la izquierda, % por la derecha; doble símbolo = coincidencia más larga.

Terminal window
echo "${f/final/inicial}" # informe.inicial.txt (primera coincidencia)
echo "${f//./_}" # informe_final_txt (todas)
Terminal window
n="Mundo"
echo "${n^^}" # MUNDO
echo "${n,,}" # mundo
echo "${n^}" # primera en mayúscula
Expresión Efecto
${v:-defecto} Usa defecto si v está vacía o sin definir (no la modifica)
${v:=defecto} Igual, pero además asigna el valor a v
${v:?mensaje} Si v está vacía, muestra el mensaje y aborta
${v:+alternativa} Usa alternativa solo si v tiene valor
Terminal window
destino="${1:-/tmp}" # primer argumento o /tmp por defecto
Terminal window
[[ $a == $b ]] # igualdad
[[ $a == pre* ]] # patrón
[[ $a =~ ^[0-9]+$ ]] # expresión regular

Colección indexada desde 0.

Terminal window
frutas=("manzana" "pera" "uva")
frutas[3]="kiwi"
frutas+=("melón") # añadir al final
Expresión Resultado
${frutas[0]} Primer elemento
${frutas[-1]} Último elemento (Bash 4.3+)
${frutas[@]} Todos los elementos
${#frutas[@]} Número de elementos
${!frutas[@]} Índices
${frutas[@]:1:2} Subconjunto (desde 1, 2 elementos)
Terminal window
for f in "${frutas[@]}"; do
echo "$f"
done
for i in "${!frutas[@]}"; do
echo "$i: ${frutas[$i]}"
done
Terminal window
unset 'frutas[1]' # eliminar un elemento (deja hueco en los índices)
copia=("${frutas[@]}") # copiar
mapfile -t lineas < fichero.txt # cada línea del fichero, un elemento
lineas=($(cat fichero.txt)) # evitar: separa por espacios y aplica globbing

Entrecomillar siempre "${array[@]}" para conservar elementos con espacios.

Pares clave-valor. Requieren declare -A (Bash 4+).

Terminal window
declare -A puertos
puertos[ssh]=22
puertos[http]=80
puertos[https]=443
echo "${puertos[ssh]}" # 22

Declaración en una línea:

bash

Terminal window
declare -A puertos=([ssh]=22 [http]=80 [https]=443)

bash

Terminal window
for servicio in "${!puertos[@]}"; do
echo "$servicio -> ${puertos[$servicio]}"
done

bash

Terminal window
echo "${#puertos[@]}" # número de pares
[[ -v puertos[ftp] ]] || echo "ftp no definido" # comprobar si existe la clave
unset 'puertos[http]' # eliminar

El orden de recorrido de un array asociativo no está garantizado.

La shell procesa cada línea aplicando expansiones en este orden aproximado:

  1. Llaves {a,b}
  2. Tilde ~
  3. Parámetros y variables $var, aritmética $(( )), sustitución de comandos $( )
  4. Separación de palabras
  5. Globbing *, ?, [...]

bash

Terminal window
echo {1..5} # 1 2 3 4 5
echo {a..e} # a b c d e
echo {01..10} # 01 02 ... 10
echo {0..20..5} # 0 5 10 15 20
echo archivo_{a,b,c}.txt # archivo_a.txt archivo_b.txt archivo_c.txt
mkdir -p proyecto/{src,docs,tests}
Terminal window
echo "Hoy es $(date +%A)"
total=$(ls | wc -l)

Se prefiere $(...) frente a las comillas invertidas `...`, porque se puede anidar y es más legible.

Terminal window
cd ~ # directorio personal
ls ~/bin
Terminal window
echo "Resultado: $((5 * 4))"

El resultado de una expansión sin comillas se separa según IFS (espacio, tabulador, salto de línea) y se le aplica globbing. Por eso se entrecomillan las variables.