Descriptores (fd -> file descriptor)
Bloqueo - Impedir ejecución simultánea
Un descriptor de archivo (fd, file descriptor) es simplemente un número entero que
el kernel usa como índice para referirse a un archivo abierto. No es el archivo en
sí, ni su ruta, ni su contenido — es solo un identificador numérico que representa
una conexión abierta entre tu proceso y ese archivo.
La mejor forma de verlo es como una tabla que el kernel mantiene por cada proceso:
Los tres primeros descriptores los tiene todo proceso desde que nace: 0 es stdin
(lo que lees del teclado), 1 es stdout (lo que se imprime en pantalla), y 2 es
stderr (los mensajes de error). Por eso >&2 en el script redirige al error están-
dar — estás escribiendo al fd número 2.
| Número | Nombre | Significado |
| 0 | stdin | Entrada estándar |
| 1 | stdout | Salida estándar |
| 2 | stderr | Salida de error |
comando > out.txt # stdout
comando 2> err.txt # stderr
comando > out.txt 2> err.txt # (1)
comando > todo.txt 2>&1 # stderr → stdout
2>&1 significa: “manda el descriptor 2 al mismo sitio que el 1”
2>&1 equivale a $> .
(1) En este caso se redirige stdout a out.txt y stderr a err.txt
Opciones:
${var:-valor} → valor por defecto (sin modificar var)
${var:=valor} → asignar valor por defecto
${var:?error} → obligatorio o error
${var:+valor} → valor alternativo si existe
Expansión unset A A="" A="x"
${A:-v} v v x
${A:=v} A=v A=v x
${A:?e} ❌ error ❌ error x
${A:+v} `` `` v
Idea clave:
# → recorta desde el inicio
% → recorta desde el final
Opciones:
${var#patrón} → recorte corto desde el inicio
${var##patrón} → recorte largo desde el inicio
${var%patrón} → recorte corto desde el final
${var%%patrón} → recorte largo desde el final
Siendo s="foo/bar/baz.txt"
Expansión Resultado
${s#*/} bar/baz.txt
${s##*/} baz.txt
${s%/*} foo/bar
${s%%/*} foo
${s%.*} foo/bar/baz
${s%%.*} foo/bar/baz
Ejemplo real en scripts
log="/var/log/nginx/error.log"
dir="${log%/*}"
file="${log##*/}"
name="${file%.*}"
echo "$dir"
echo "$file"
echo "$name"
Salida:
/var/log/nginx
error.log
error
Quitar doble extensión (.tar.gz)
name="${file%%.*}"
Para impedir la ejecución simultánea de un script se puede emplear el siguiente có-
digo:
SCRIPT_REAL=$(realpath "$0") # resuelve symlinks
SCRIPT_NAME=$(basename "$SCRIPT_REAL") # nombre del archivo real
SCRIPT_NAME="${SCRIPT_NAME%.*}" # sin extensión
SCRIPT_NAME="${SCRIPT_NAME// /_}" # sustituir espacios vacíos por _
LOCKFILE="/var/lock/${SCRIPT_NAME}.lock"
flock -n "$LOCKFD" || {
echo "${SCRIPT_NAME} ya en ejecución, saliendo." >&2
exit 1
# tu código aquí
) {LOCKFD}>"$LOCKFILE"
usan patrones glob, no regex (* ? [...])
1️⃣ ${var#patrón} → recorte corto desde el inicio
path="/usr/local/bin/bash"
echo "${path#*/}"
Resultado:
usr/local/bin/bash
👉 Quita la coincidencia más corta del patrón desde el principio.
2️⃣ ${var##patrón} → recorte largo desde el inicio
echo "${path##*/}"
Resultado:
bash
👉 Quita la coincidencia más larga → muy usado para obtener el nombre de archivo.
📌 Equivale mentalmente a:
basename "$path"
3️⃣ ${var%patrón} → recorte corto desde el final
file="archivo.tar.gz"
echo "${file%.*}"
Resultado:
archivo.tar
👉 Quita la coincidencia más corta desde el final.
4️⃣ ${var%%patrón} → recorte largo desde el final
echo "${file%%.*}"
Resultado:
archivo
👉 Quita la coincidencia más larga desde el final.
📌 Equivale mentalmente a:
${file%%.*} # nombre sin ninguna extensión
🧠 Regla visual ultra rápida
# → izquierda
% → derecha
uno = corto
doble = largo
🧪 Tabla resumen
s="foo/bar/baz.txt"
Expansión Resultado
${s#*/} bar/baz.txt
${s##*/} baz.txt
${s%/*} foo/bar
${s%%/*} foo
${s%.*} foo/bar/baz
${s%%.*} foo/bar/baz
🧩 Patrones comunes (los que realmente usarás)
📁 Nombre de archivo
base="${path##*/}"
📂 Directorio padre
dir="${path%/*}"
📄 Quitar extensión
name="${file%.*}"
🧷 Quitar doble extensión (.tar.gz)
name="${file%%.*}"
⚠️ Importante: no es regex
Esto NO funciona:
${var#^foo}
Esto SÍ funciona:
${var#foo*}
Patrones válidos:
* cualquier cosa
? un carácter
[abc] clase de caracteres
💡 Ejemplo real en scripts
log="/var/log/nginx/error.log"
dir="${log%/*}"
file="${log##*/}"
name="${file%.*}"
echo "$dir"
echo "$file"
echo "$name"
Salida:
/var/log/nginx
error.log
error
🧠 Cuándo usarlos (regla práctica)
¿Manipulas strings simples? → ${#%}
¿Necesitas regex complejas? → sed / awk
¿Rendimiento y limpieza? → expansión Bash siempre
echo "${FOO:-bar}"
Si FOO no existe o está vacía → imprime bar
Si FOO existe → imprime su valor
NO asigna nada a FOO
Útil para:
Valores opcionales
Evitar fallos con set -u
2️⃣ ${var:=valor} → asignar valor por defecto
: "${FOO:=bar}"
Si FOO no existe o está vacía → FOO pasa a valer bar
Si existe → no cambia
📌 Muy usado para:
configuración por defecto
inicialización perezosa
3️⃣ ${var:?error} → obligatorio o error
file="${1:?Falta el archivo}"
Si la variable no existe o está vacía:
imprime el mensaje
aborta ejecución
📌 Ideal para:
argumentos obligatorios
contratos claros de funciones
4️⃣ ${var:+valor} → valor alternativo si existe
echo "${DEBUG:+--debug}"
Si DEBUG existe y no está vacía → imprime --debug
Si no → imprime nada
📌 Perfecto para:
flags opcionales
construcción de comandos
🧪 Comparación rápida
Supongamos:
unset A
A=""
A="x"
Expansión unset A A="" A="x"
${A:-v} v v x
${A:=v} A=v A=v x
${A:?e} ❌ error ❌ error x
${A:+v} `` `` v
🧠 Regla mental ultra simple
Quiero valor por defecto → :-
Quiero asignar si falta → :=
Es obligatorio → :?
Flag opcional → :+
💥 Error típico con set -u
echo "$VAR"
# bash: VAR: unbound variable
Solución segura:
echo "${VAR:-}"