0Pricing
Linux Command Line & Bash Scripting Mastery · Lección

Archivos temporales seguros y directorios de bloqueo

Use mktemp y flock para crear recursos temporales sin condiciones de carrera y evitar ejecuciones simultáneas de scripts.

Archivos temporales seguros y directorios de bloqueo es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.

Por qué los archivos temporales suponen un riesgo de seguridad

Los scripts de Bash suelen necesitar almacenamiento temporal: resultados intermedios, indicadores de bloqueo y áreas de preparación. Sin embargo, crear archivos temporales sin cuidado abre vulnerabilidades graves.

  • Condiciones de carrera: otro proceso puede predecir el nombre del archivo y crearlo primero, redirigiendo sus escrituras.
  • Ataques mediante enlaces simbólicos: un atacante crea un enlace simbólico en la ruta prevista que apunta a un archivo confidencial como /etc/passwd.
  • Archivos residuales: si un script falla, los archivos temporales se acumulan y pueden exponer datos confidenciales.

Las dos herramientas principales que eliminan estos problemas son mktemp y flock. En esta lección aprenderá a utilizar ambas de forma segura y defensiva.

Creación de archivos temporales seguros con mktemp

mktemp crea un archivo temporal con un nombre aleatorio e impredecible y devuelve su ruta. Crea el archivo de forma atómica, por lo que ningún otro proceso puede apropiarse antes del nombre.

  • Sintaxis: mktemp [TEMPLATE]; la plantilla debe terminar con al menos tres caracteres X.
  • Cada X se sustituye por un carácter aleatorio, lo que produce un nombre único como /tmp/script.aB3kQz.
  • El archivo se crea automáticamente con permisos 0600 (solo el propietario puede leerlo).

Capture siempre la ruta devuelta en una variable inmediatamente para poder utilizarla y limpiarla posteriormente.

#!/usr/bin/env bash
set -euo pipefail

# Create a secure temp file
TMPFILE=$(mktemp /tmp/myapp.XXXXXX)
echo "Temp file created at: $TMPFILE"

# Write data to it
echo "some intermediate result" > "$TMPFILE"

# Read it back
cat "$TMPFILE"

# Clean up
rm -f "$TMPFILE"

Limpie siempre con una trap

Si el script termina inesperadamente — debido a un error, una señal o la activación de set -e— los archivos temporales quedarán atrás a menos que registre un manejador de limpieza.

El comando integrado trap ejecuta un comando cuando el shell recibe una señal o termina. El patrón habitual para limpiar archivos temporales es:

  • Registre la trap inmediatamente después de crear el archivo temporal.
  • Use una trap para EXIT de modo que la limpieza se ejecute tanto al salir con normalidad como de forma anómala.
  • Registre también INT y TERM si el script se ejecuta durante mucho tiempo o es interactivo.

Esto garantiza que no queden archivos huérfanos, incluso si el script se termina durante la ejecución.

#!/usr/bin/env bash
set -euo pipefail

TMPFILE=$(mktemp /tmp/report.XXXXXX)

# Register cleanup before doing any real work
cleanup() {
    rm -f "$TMPFILE"
    echo "Cleaned up $TMPFILE" >&2
}
trap cleanup EXIT

# Do work — even if this fails, cleanup() will run
echo "Processing..." > "$TMPFILE"
grep "result" "$TMPFILE" || true

echo "Done. File will be removed on exit."

Creación de directorios temporales con mktemp -d

A veces necesita un directorio completo para preparar varios archivos; por ejemplo, para crear un archivo comprimido o extraer un tarball antes de procesarlo. Use mktemp -d para crear un directorio temporal seguro.

  • El directorio se crea con los permisos 0700 (acceso exclusivo del propietario).
  • Límpielo con rm -rf en su trap; tenga cuidado de eliminar únicamente la variable, nunca una ruta codificada directamente.
  • Use comillas dobles y compruebe que la variable no esté vacía antes de llamar a rm -rf como medida de seguridad adicional.
#!/usr/bin/env bash
set -euo pipefail

TMPDIR=$(mktemp -d /tmp/extract.XXXXXX)

cleanup() {
    # Guard: only rm if variable is set and non-empty
    [[ -n "${TMPDIR:-}" ]] && rm -rf "$TMPDIR"
}
trap cleanup EXIT

echo "Working in $TMPDIR"

# Simulate staging files
echo "file one" > "$TMPDIR/part1.txt"
echo "file two" > "$TMPDIR/part2.txt"

ls "$TMPDIR"
echo "All done."

El problema de las ejecuciones simultáneas de scripts

Los trabajos de Cron, los temporizadores de systemd y los scripts ejecutados manualmente pueden iniciar fácilmente varias instancias del mismo script al mismo tiempo. Esto provoca:

  • Procesamiento duplicado: los mismos registros de la base de datos o archivos se procesan dos veces.
  • Salida dañada: dos instancias escriben simultáneamente en el mismo archivo de salida.
  • Interbloqueos o estado parcial: ambas instancias modifican recursos compartidos en un orden intercalado e impredecible.

La solución tradicional consistía en escribir un archivo PID y comprobarlo al iniciar, pero este enfoque presenta una ventana de condición de carrera entre la comprobación y la escritura. La solución moderna correcta es flock, que utiliza el mecanismo de bloqueo consultivo del kernel para garantizar un bloqueo sin condiciones de carrera.

Bloqueo con flock: el patrón de una sola línea

flock adquiere un bloqueo consultivo sobre un descriptor de archivo antes de ejecutar un comando. El uso más sencillo consiste en envolver todo el script desde la línea de comandos:

flock -n /var/lock/myscript.lock bash myscript.sh

  • -n (sin bloqueo): termina inmediatamente con el estado 1 si el bloqueo ya está ocupado, en lugar de esperar.
  • Sin -n, flock se bloquea hasta que el bloqueo está disponible, lo que resulta útil para poner ejecuciones en cola.
  • El archivo de bloqueo solo es un marcador; su contenido no importa. Es seguro conservarlo entre ejecuciones.
  • Cuando termina el proceso que mantiene el bloqueo, el kernel lo libera automáticamente; no es necesario realizar una limpieza manual.
#!/usr/bin/env bash
# launcher.sh — prevents concurrent runs of worker.sh
set -euo pipefail

LOCKFILE="/tmp/myworker.lock"

if ! flock -n "$LOCKFILE" bash -c 'echo "Running worker..."; sleep 2; echo "Done."'; then
    echo "Another instance is already running. Exiting." >&2
    exit 1
fi

flock dentro de un script mediante un descriptor de archivo

Para realizar el bloqueo dentro de un script en lugar de envolverlo desde fuera, use exec para abrir un descriptor de archivo y después llame a flock sobre ese descriptor. Este es el patrón idiomático utilizado en scripts de producción.

  • exec 200>"$LOCKFILE" abre el archivo en el descriptor 200 para escritura (y lo crea si es necesario).
  • flock -n 200 intenta bloquear el descriptor 200 sin bloqueo.
  • Como el bloqueo está asociado al descriptor de archivo (no al nombre del archivo), se libera automáticamente cuando termina el proceso del shell.
  • Convencionalmente se utilizan los números de descriptor del 200 al 299 para evitar conflictos con stdin/stdout/stderr.
#!/usr/bin/env bash
set -euo pipefail

LOCKFILE="/tmp/myjob.lock"

# Open lock file on FD 200
exec 200>"$LOCKFILE"

# Attempt non-blocking lock
if ! flock -n 200; then
    echo "ERROR: Another instance of this script is running." >&2
    exit 1
fi

echo "Lock acquired. Starting work..."
sleep 1
echo "Work complete. Lock will be released on exit."

Combinación de mktemp y flock en un solo script

Los scripts defensivos reales necesitan ambas cosas: un bloqueo para impedir ejecuciones simultáneas y archivos temporales seguros para los datos intermedios. Este es el patrón completo que combina ambas técnicas:

  • Adquiera primero el bloqueo, antes de crear archivos temporales, para que solo una instancia realice cualquier trabajo.
  • Cree los recursos temporales después de confirmar el bloqueo.
  • Registre el trap inmediatamente después de crear los temporales para garantizar la limpieza independientemente de cómo termine el script.
  • El archivo de bloqueo nunca debe estar en el directorio temporal: debe persistir entre ejecuciones para que flock pueda hacer referencia a él.
#!/usr/bin/env bash
set -euo pipefail

LOCKFILE="/tmp/report_builder.lock"
exec 200>"$LOCKFILE"

if ! flock -n 200; then
    echo "Already running — aborting." >&2
    exit 1
fi

# Now safe to create temp resources
TMPDIR=$(mktemp -d /tmp/report.XXXXXX)
TMPLOG=$(mktemp /tmp/report_log.XXXXXX)

cleanup() {
    rm -rf "${TMPDIR:-}"
    rm -f  "${TMPLOG:-}"
}
trap cleanup EXIT

echo "Building report in $TMPDIR" | tee "$TMPLOG"
echo "Step 1 complete"             >> "$TMPLOG"
cat "$TMPLOG"

Directorios de bloqueo como mecanismo alternativo

En sistemas donde flock no está disponible (algunos sistemas embebidos o sistemas de archivos de red como NFS), puede utilizar directorios de bloqueo. mkdir es atómico en sistemas POSIX: solo se ejecuta correctamente si el directorio aún no existe.

  • Cree el directorio de bloqueo con mkdir /tmp/myscript.lock.d; si otra instancia ya lo creó, mkdir falla inmediatamente.
  • Almacene metadatos (como el PID) dentro del directorio para facilitar el diagnóstico.
  • Elimine siempre el directorio en un trap asociado a EXIT.
  • Advertencia: a diferencia de flock, un bloqueo mediante directorio NO se libera automáticamente si el proceso termina con -9 o la máquina se reinicia; añada una comprobación para detectar bloqueos obsoletos.
#!/usr/bin/env bash
set -euo pipefail

LOCKDIR="/tmp/myscript.lock.d"

# Atomic mkdir — fails if directory already exists
if ! mkdir "$LOCKDIR" 2>/dev/null; then
    # Check if the holding PID is still alive
    HOLDER_PID=$(cat "$LOCKDIR/pid" 2>/dev/null || echo "")
    if [[ -n "$HOLDER_PID" ]] && kill -0 "$HOLDER_PID" 2>/dev/null; then
        echo "Locked by PID $HOLDER_PID. Exiting." >&2
        exit 1
    else
        echo "Stale lock detected. Removing and continuing." >&2
        rm -rf "$LOCKDIR"
        mkdir "$LOCKDIR"
    fi
fi

echo "$$" > "$LOCKDIR/pid"
trap 'rm -rf "$LOCKDIR"' EXIT

echo "Lock acquired via directory. Running..."
sleep 1
echo "Done."

Espera con tiempo de espera mediante flock

A veces querrá esperar a que se libere un bloqueo en lugar de fallar inmediatamente, pero sin esperar indefinidamente. flock admite un tiempo de espera mediante la opción -w.

  • flock -w 10 200 espera hasta 10 segundos para obtener el bloqueo y después termina con el estado 1 si sigue sin estar disponible.
  • Esto es ideal para scripts que deben ponerse en cola detrás de un proceso anterior de corta duración, pero abandonar el intento si dicho proceso se ha quedado bloqueado.
  • Combine -w con un mensaje de error significativo que incluya el contexto —la ruta del archivo de bloqueo y el tiempo de espera— para que los operadores puedan diagnosticar rápidamente los bloqueos.
#!/usr/bin/env bash
set -euo pipefail

LOCKFILE="/tmp/data_sync.lock"
TIMEOUT=15

exec 200>"$LOCKFILE"

echo "Waiting up to ${TIMEOUT}s for lock on $LOCKFILE..."

if ! flock -w "$TIMEOUT" 200; then
    echo "ERROR: Could not acquire lock after ${TIMEOUT}s." \
         "Another instance may be hung." >&2
    exit 1
fi

echo "Lock acquired. Syncing data..."
sleep 1
echo "Sync complete."

Lista de comprobación defensiva: recursos temporales seguros

Antes de distribuir cualquier script que utilice archivos temporales o bloqueos, repase esta lista de comprobación:

  • Use mktemp, nunca rutas codificadas directamente: /tmp/myapp.tmp es predecible y puede explotarse.
  • Capture la ruta inmediatamente: TMPFILE=$(mktemp ...) antes de cualquier otro comando.
  • Registre trap cleanup EXIT inmediatamente después de la creación: no al final del script.
  • Escriba entre comillas dobles todos los usos de variables: rm -f "$TMPFILE", nunca rm -f $TMPFILE.
  • Prefiera flock a los archivos PID: el kernel lo administra y lo libera automáticamente en caso de fallo.
  • Use -n sin bloqueo de forma predeterminada: los bloqueos silenciosos que esperan ocultan problemas de rendimiento.
  • Coloque el archivo de bloqueo fuera del directorio temporal: así sobrevivirá al trap de limpieza.
  • Pruebe el comportamiento de limpieza: ejecute el script y use kill -9 durante su ejecución; compruebe que no queden archivos residuales (en scripts basados en flock; los bloqueos mediante directorios requieren atención adicional).

Comprobación de conocimientos: comportamiento de las opciones de flock

Un trabajo de Cron se ejecuta cada minuto y procesa un archivo compartido. Quiere que cualquier nueva invocación termine inmediatamente con un error si una ejecución anterior sigue activa, sin esperar. ¿Qué invocación de flock implementa esto correctamente?

Repaso: archivos temporales seguros y directorios de bloqueo

En esta lección aprendió las dos herramientas esenciales para la gestión defensiva de recursos en Bash:

  • mktemp crea archivos temporales con nombres impredecibles y permisos seguros (0600), así como directorios (0700), eliminando las condiciones de carrera y los ataques mediante enlaces simbólicos que afectan a las rutas codificadas directamente.
  • trap cleanup EXIT garantiza la eliminación de los archivos temporales al salir, ya sea normalmente, por un error o debido a una señal, siempre que se registre inmediatamente después de la creación.
  • flock proporciona un bloqueo consultivo impuesto por el kernel: use -n para fallar rápidamente si hay contención, -w N para esperar con un tiempo límite y el patrón exec 200>file para bloquear dentro del script, de modo que el kernel libere el bloqueo automáticamente al terminar el proceso.
  • Los directorios de bloqueo (mkdir) ofrecen una alternativa portable para entornos donde flock no está disponible, pero requieren una detección explícita de bloqueos obsoletos.
  • Mantenga siempre el archivo de bloqueo fuera del directorio temporal y escriba entre comillas dobles todas las variables utilizadas durante la limpieza.

La combinación de mktemp + flock + trap permite crear scripts protegidos frente a invocaciones simultáneas, fallos impredecibles y manipulaciones maliciosas del sistema de archivos.

Preguntas frecuentes

¿La lección «Archivos temporales seguros y directorios de bloqueo» es gratis?

Sí — el texto completo de «Archivos temporales seguros y directorios de bloqueo» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.

¿Qué aprenderé en «Archivos temporales seguros y directorios de bloqueo»?

Use mktemp y flock para crear recursos temporales sin condiciones de carrera y evitar ejecuciones simultáneas de scripts. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?

No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Archivos temporales seguros y directorios de bloqueo»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?

Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Modo estricto con set -euo pipefail
  2. Controladores trap para limpieza y señales
  3. Archivos temporales seguros y directorios de bloqueo
  4. Scripts idempotentes y lógica de reintento con backoff
← Volver a Linux Command Line & Bash Scripting Mastery