File temporanei sicuri e directory di lock
Usi mktemp e flock per creare risorse temporanee senza race condition e impedire l'esecuzione simultanea degli script.
File temporanei sicuri e directory di lock è una lezione DevOps Bootcamp gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DevOps Bootcamp, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DevOps Bootcamp include 4 lezioni in totale.
Perché i file temporanei sono un rischio per la sicurezza
Gli script Bash hanno spesso bisogno di spazio temporaneo per risultati intermedi, indicatori di lock e aree di staging. Tuttavia, creare file temporanei senza le dovute precauzioni espone a gravi vulnerabilità.
- Race condition: un altro processo può prevedere il nome del file e crearlo per primo, reindirizzando le scritture.
- Attacchi tramite symlink: un attaccante crea un symlink nel percorso previsto, indirizzandolo a un file sensibile come
/etc/passwd. - File residui: se uno script va in crash, i file temporanei si accumulano e possono esporre dati sensibili.
I due strumenti fondamentali che eliminano questi problemi sono mktemp e flock. In questa lezione imparerà a usare entrambi in modo sicuro e difensivo.
Creare file temporanei sicuri con mktemp
mktemp crea un file temporaneo con un nome casuale e imprevedibile e ne restituisce il percorso. Crea il file in modo atomico, impedendo a qualsiasi altro processo di appropriarsi prima del nome.
- Sintassi:
mktemp [TEMPLATE]— il modello deve terminare con almeno tre caratteriX. - Ogni
Xviene sostituita da un carattere casuale, producendo un nome univoco come/tmp/script.aB3kQz. - Il file viene creato automaticamente con permessi
0600(leggibile solo dal proprietario).
Acquisisca sempre immediatamente il percorso restituito in una variabile, così da poterlo usare e rimuovere in seguito.
#!/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"Eseguire sempre la pulizia con un trap
Se lo script termina in modo imprevisto — a causa di un errore, di un segnale o dell'attivazione di set -e — i file temporanei rimarranno, a meno che non registri un gestore di pulizia.
Il builtin trap esegue un comando quando la shell riceve un segnale o termina. Lo schema canonico per la pulizia dei file temporanei è:
- Registri il trap subito dopo aver creato il file temporaneo.
- Imposti il trap su
EXIT, affinché la pulizia venga eseguita sia in caso di uscita normale sia anomala. - Imposti inoltre il trap su
INTeTERMse lo script ha un'esecuzione lunga o è interattivo.
In questo modo non rimarranno file orfani, anche se lo script viene terminato durante l'esecuzione.
#!/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."Creazione di directory temporanee con mktemp -d
A volte è necessaria un’intera directory per preparare più file, ad esempio per creare un archivio o estrarre un tarball prima dell’elaborazione. Utilizzi mktemp -d per creare una directory temporanea sicura.
- La directory viene creata con i permessi
0700(accesso esclusivo al proprietario). - Esegua la pulizia con
rm -rfnel proprio trap: presti attenzione a rimuovere solo la variabile, mai un percorso codificato direttamente. - Utilizzi le virgolette doppie e verifichi che la variabile non sia vuota prima di chiamare
rm -rf, come ulteriore controllo di sicurezza.
#!/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."Il problema delle esecuzioni simultanee degli script
I cron job, i timer di systemd e gli script avviati manualmente possono facilmente eseguire contemporaneamente più istanze dello stesso script. Questo causa:
- Elaborazione duplicata: gli stessi record del database o gli stessi file vengono elaborati due volte.
- Output corrotto: due istanze scrivono contemporaneamente nello stesso file di output.
- Deadlock o stato parziale: entrambe le istanze modificano risorse condivise in un ordine intercalato e imprevedibile.
La soluzione tradizionale consisteva nello scrivere un file PID e controllarlo all’avvio, ma questo approccio presenta una finestra di race condition tra il controllo e la scrittura. La soluzione moderna corretta è flock, che utilizza il meccanismo di locking consultivo del kernel per garantire un lock privo di race condition.
Locking con flock: il pattern su una riga
flock acquisisce un lock consultivo su un file descriptor prima di eseguire un comando. L’utilizzo più semplice consiste nell’avvolgere l’intero script dalla riga di comando:
flock -n /var/lock/myscript.lock bash myscript.sh
-n(non bloccante): termina immediatamente con stato 1 se il lock è già acquisito, invece di attendere.- Senza
-n,flockresta in attesa finché il lock non diventa disponibile: utile per mettere le esecuzioni in coda. - Il file di lock è soltanto un indicatore; il suo contenuto non è importante. È sicuro conservarlo tra un’esecuzione e l’altra.
- Quando il processo che detiene il lock termina, il kernel lo rilascia automaticamente: non è necessaria alcuna pulizia manuale.
#!/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
fiflock all’interno di uno script tramite un file descriptor
Per applicare il locking all’interno di uno script invece di avvolgerlo dall’esterno, utilizzi exec per aprire un file descriptor e chiami quindi flock su quel descriptor. Questo è il pattern idiomatico utilizzato negli script di produzione.
exec 200>"$LOCKFILE"apre il file sul descriptor 200 in scrittura (creandolo se necessario).flock -n 200prova ad acquisire senza blocco il lock sul descriptor 200.- Poiché il lock è associato al file descriptor, non al nome del file, viene rilasciato automaticamente quando il processo della shell termina.
- Per convenzione, i numeri di descriptor da 200 a 299 vengono utilizzati per evitare conflitti 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."Combinare mktemp e flock in un unico script
Gli script realmente difensivi hanno bisogno di entrambi: un lock per impedire le esecuzioni simultanee e file temporanei sicuri per i dati intermedi. Ecco il pattern completo che combina entrambe le tecniche:
- Acquisisca prima il lock, prima di creare qualsiasi file temporaneo, in modo che una sola istanza esegua il lavoro.
- Crei le risorse temporanee dopo aver verificato che il lock sia stato acquisito.
- Registri il
trapimmediatamente dopo aver creato le risorse temporanee, così la pulizia è garantita indipendentemente dal modo in cui termina lo script. - Il file di lock non deve mai trovarsi nella directory temporanea: deve rimanere tra un’esecuzione e l’altra affinché
flockpossa farvi riferimento.
#!/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"Le directory di lock come meccanismo alternativo
Nei sistemi in cui flock non è disponibile, ad esempio in alcuni sistemi embedded o nei filesystem di rete come NFS, è possibile utilizzare le directory di lock. Sui sistemi POSIX, mkdir è atomico: ha esito positivo solo se la directory non esiste già.
- Crei la directory di lock con
mkdir /tmp/myscript.lock.d: se un’altra istanza l’ha già creata,mkdirfallisce immediatamente. - Memorizzi nella directory i metadati, come il PID, per facilitare la diagnostica.
- Rimuova sempre la directory in un
trapsuEXIT. - Attenzione: a differenza di
flock, una directory di lock NON viene rilasciata automaticamente se il processo viene terminato con-9o se la macchina viene riavviata: aggiunga un controllo per rilevare i lock obsoleti.
#!/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."Attesa basata sul timeout con flock
A volte è necessario attendere un lock invece di fallire immediatamente, ma senza aspettare all’infinito. flock supporta un timeout tramite il flag -w.
flock -w 10 200attende il lock per un massimo di 10 secondi, quindi termina con stato 1 se non è ancora disponibile.- È ideale per gli script che devono accodarsi dietro a un’esecuzione precedente di breve durata, ma rinunciare se quest’ultima è bloccata.
- Combini
-wcon un messaggio di errore significativo che includa il contesto, ovvero il percorso del file di lock e la durata dell’attesa, così gli operatori possono diagnosticare rapidamente i blocchi.
#!/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."Checklist difensiva: risorse temporanee sicure
Prima di distribuire uno script che utilizza file temporanei o locking, verifichi i seguenti punti:
- Utilizzi
mktemp, mai percorsi codificati direttamente:/tmp/myapp.tmpè prevedibile e può essere sfruttato. - Salvi immediatamente il percorso:
TMPFILE=$(mktemp ...)prima di qualsiasi altro comando. - Registri
trap cleanup EXITimmediatamente dopo la creazione: non alla fine dello script. - :
rm -f "$TMPFILE", mairm -f $TMPFILE. - Preferisca
flockai file PID: è gestito dal kernel e viene rilasciato automaticamente in caso di crash. - Utilizzi
-nnon bloccante come impostazione predefinita: i lock bloccanti e silenziosi nascondono i problemi di prestazioni. - Collochi il file di lock fuori dalla directory temporanea: così sopravvive al trap di pulizia.
- Verifichi il comportamento della pulizia: esegua lo script e utilizzi
kill -9durante l’esecuzione; verifichi che non rimangano file residui (per gli script basati suflock; le directory di lock richiedono maggiore attenzione).
Verifica delle conoscenze: comportamento dei flag di flock
Un cron job viene eseguito ogni minuto ed elabora un file condiviso. Si desidera che ogni nuova invocazione termini immediatamente con un errore se un’esecuzione precedente è ancora attiva, senza attendere. Quale invocazione di flock implementa correttamente questo comportamento?
Riepilogo: file temporanei sicuri e directory di lock
In questa lezione ha appreso i due strumenti essenziali per la gestione difensiva delle risorse in Bash:
mktempcrea file temporanei con nomi imprevedibili e permessi sicuri (0600) e directory temporanee (0700), eliminando le race condition e gli attacchi tramite link simbolici che affliggono i percorsi codificati direttamente.trap cleanup EXITgarantisce la rimozione dei file temporanei a ogni terminazione, normale, dovuta a un errore o a un segnale, quando viene registrato immediatamente dopo la creazione.flockfornisce un locking consultivo applicato dal kernel: utilizzi-nper fallire rapidamente in caso di contesa,-w Nper attendere con un timeout e il patternexec 200>fileper il locking all’interno dello script, che il kernel rilascia automaticamente alla terminazione del processo.- Le directory di lock (
mkdir) offrono un’alternativa portabile negli ambienti in cuiflocknon è disponibile, ma richiedono un controllo esplicito dei lock obsoleti. - Mantenga sempre il file di lock fuori dalla directory temporanea e racchiuda tra virgolette doppie ogni variabile utilizzata nella pulizia.
La combinazione di mktemp + flock + trap consente di creare script sicuri contro invocazioni simultanee, crash imprevedibili e manipolazioni malevole del filesystem.
Domande Frequenti
La lezione «File temporanei sicuri e directory di lock» è gratuita?
Sì — il testo completo di «File temporanei sicuri e directory di lock» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DevOps Bootcamp, passa a CoddyKit PRO. Il corso DevOps Bootcamp include 4 lezioni in totale.
Cosa imparerò in «File temporanei sicuri e directory di lock»?
Usi mktemp e flock per creare risorse temporanee senza race condition e impedire l'esecuzione simultanea degli script. Eserciti DevOps Bootcamp con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare DevOps Bootcamp?
Non è richiesta alcuna esperienza precedente. DevOps Bootcamp su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.
Quanto tempo richiede la lezione «File temporanei sicuri e directory di lock»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione DevOps Bootcamp?
Sì. Ogni lezione DevOps Bootcamp include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Modalità rigorosa con set -euo pipefail
- Gestori trap per pulizia e segnali
- File temporanei sicuri e directory di lock
- Script idempotenti e logica di retry con backoff