2009-03-26 13 views
60

Sto creando file temporanei da uno script bash. Li sto eliminando alla fine dell'elaborazione, ma poiché lo script è in esecuzione da un po 'di tempo, se lo uccido o semplicemente CTRL-C durante l'esecuzione, i file temporanei non vengono cancellati.
C'è un modo per rilevare quegli eventi e ripulire i file prima che l'esecuzione finisca?Rimozione di file temporanei creati in bash inaspettata exit

Inoltre, esiste una sorta di best practice per la denominazione e la posizione di tali file temporanei?
Non sono attualmente sicuro tra l'uso:

TMP1=`mktemp -p /tmp` 
TMP2=`mktemp -p /tmp` 
... 

e

TMP1=/tmp/`basename $0`1.$$ 
TMP2=/tmp/`basename $0`2.$$ 
... 

O forse c'è qualche soluzioni migliori?

risposta

55

È possibile impostare un "trap" da eseguire all'uscita o su un control-c per eseguire la pulizia.

trap "{ rm -f $LOCKFILE; }" EXIT 

In alternativa, uno dei miei preferiti unix-ismi è quello di aprire un file, e quindi eliminarlo mentre hai ancora aperto. Il file rimane nel file system e puoi leggerlo e scriverlo, ma non appena il tuo programma si chiude, il file scompare. Non sei sicuro di come lo faresti in bash, però.

BTW: Un argomento che darò a mktemp anziché utilizzare la propria soluzione: se l'utente prevede che il programma creerà file temporanei enormi, potrebbe voler impostare TMPDIR in un punto più grande, come/var/tmp. mktemp riconosce che, la tua soluzione arrotolata a mano (seconda opzione) no. Io uso spesso TMPDIR=/var/tmp gvim -d foo bar, per esempio.

+7

Con Bash, 'exec 5 <> $ TMPFILE' lega il descrittore di file da 5 a $ TMPFILE come lettura-scrittura ed è possibile utilizzare '<&5', '> & 5', e'/proc/$$/fd/5' (Linux) in seguito. L'unico problema è che Bash non ha la funzione 'seek' ... – ephemient

+0

Hai accettato la tua risposta dal momento che il link che hai fornito è ciò che spiega il meglio di ciò di cui avevo bisogno. Grazie – skinp

+3

Un paio di note su 'trap': non c'è modo di intrappolare' SIGKILL' (in base alla progettazione, poiché termina immediatamente l'esecuzione). Quindi, se ciò potrebbe accadere, avere un piano di fallback (come 'tmpreaper'). In secondo luogo, le trappole non sono cumulative - se hai più di un'azione da eseguire, devono essere tutte nel comando "trap". Un modo per far fronte a più azioni di pulizia è definire una funzione (e ridefinirla mentre il programma procede, se necessario) e fare riferimento a questo: 'trap cleanup_function EXIT'. –

-4

Non devi preoccuparti di rimuovere quei file tmp creati con mktemp. Verranno cancellati comunque in seguito.

Utilizzare mktemp se è possibile poiché genera più file univoci, quindi il prefisso '$$'. E sembra più un modo multipiattaforma per creare file temporanei, quindi metterli esplicitamente in/tmp.

+4

Cancellato da chi o cosa? – innaM

+0

Eliminato dall'operazione | file system stesso dopo un certo periodo di tempo –

+4

Magic? Un cronjob? O una macchina Solaris riavviata? – innaM

16

Si desidera utilizzare il comando trap per gestire l'uscita dallo script o segnali come CTRL-C. Vedi lo Advanced Bash Scripting Guide per i dettagli.

Per i vostri file temporanei, utilizzando basename $0 è una buona idea, oltre a fornire un modello che fornisce la stanza per i file temporanei sufficienti:

tempfile() { 
    tempprefix=$(basename "$0") 
    mktemp /tmp/${tempprefix}.XXXXXX 
} 

TMP1=$(tempfile) 
TMP2=$(tempfile) 

trap 'rm -f $TMP1 $TMP2' EXIT 
+0

Non intercettare su TERM/INT. Trap su EXIT. Provare a prevedere la condizione di uscita in base ai segnali ricevuti è sciocco e sicuramente non è un catchall. – lhunath

+2

Punto secondario: Usa $() invece dei singoli apici inversi. E metti doppi apici intorno a $ 0 perché potrebbe contenere spazi. –

+0

Bene, i backtick funzionano bene in questo commento, ma questo è un punto giusto, è bello avere l'abitudine di usare '$()'. Aggiunti anche i doppi apici. –

88

Io di solito creare una directory in cui collocare tutti i miei file temporanei, e subito dopo, creare un gestore EXIT per pulire questa directory quando lo script termina.

MYTMPDIR=$(mktemp -d) 
trap "rm -rf $MYTMPDIR" EXIT 

Se si mettono tutti i file temporanei sotto $MYTMPDIR, poi saranno tutti cancellati quando lo script esce in più circostanze. Uccidere un processo con SIGKILL (kill -9) uccide il processo subito, quindi il gestore EXIT non verrà eseguito in quel caso.

+20

+1 Definitivamente usa una trappola su EXIT, non sciocca TERM/INT/HUP/qualsiasi altra cosa tu possa pensare. Tuttavia, ricorda di ** quotare ** le espansioni dei parametri e vorrei * anche * raccomandare * singolo * citare la tua trappola: trap 'rm -rf "$ TMPDIR"' ESCI – lhunath

+4

Citazioni singole, perché allora la tua trappola sarà funziona ancora se in seguito nel tuo script decidi di ripulire e modificare TMPDIR a causa di circostanze. – lhunath

+10

Punto minore: Usa $() invece di singoli apici inversi. –

3

L'alternativa di utilizzare un nome di file prevedibile con $$ è un buco di sicurezza enorme e non si dovrebbe mai e poi mai mai pensare di usarlo. Anche se è solo un semplice script personale sul tuo PC utente singolo. È una pessima abitudine che non dovresti ottenere. BugTraq è pieno di incidenti "file temporanei non sicuri".Vedere here, here e here per ulteriori informazioni sull'aspetto di sicurezza dei file temporanei.

Inizialmente stavo pensando di citare gli insicuri incarichi TMP1 e TMP2, ma ripensandoci probabilmente sarebbe stato not be a good idea.

1

Io preferisco usare tempfile che crea un file in/tmp in modo sicuro e tu non devi preoccuparti di sua denominazione:

tmp=$(tempfile -s "your_sufix") 
trap "rm -f '$tmp'" exit 
+0

tempfile è purtroppo molto non trasportabile anche se più sicuro, quindi è spesso meglio evitarlo o almeno emularlo. – lericson

3

Basta tenere a mente che scelto risposta è bashism, il che significa che la soluzione come

trap "{ rm -f $LOCKFILE }" EXIT 

avrebbe funzionato solo in bash (che non prenderà Ctrl + c se shell è dash o classico sh), ma se volete la compatibilità allora è ancora necessario per enumerare tutti i segnali che si vogliono tr ap.

Ricordare inoltre che quando lo script esce dal trap per il segnale "0" (alias EXIT) viene sempre eseguita con conseguente esecuzione doppia del comando trap.

Che la ragione per non impilare tutti i segnali in una riga se c'è il segnale di uscita.

Per comprendere meglio guardare seguente script che funzionerà su sistemi diversi, senza modifiche:

#!/bin/sh 

on_exit() { 
    echo 'Cleaning up...(remove tmp files, etc)' 
} 

on_preExit() { 
    echo 
    echo 'Exiting...' # Runs just before actual exit, 
        # shell will execute EXIT(0) after finishing this function 
        # that we hook also in on_exit function 
    exit 2 
} 


trap on_exit EXIT       # EXIT = 0 
trap on_preExit HUP INT QUIT TERM STOP PWR # 1 2 3 15 30 


sleep 3 # some actual code... 

exit 

Questa soluzione vi darà un maggiore controllo in quanto è possibile eseguire alcune delle vostro codice al verificarsi di segnale effettivo poco prima uscita finale (funzione preExit) e se necessario è possibile eseguire qualche codice al segnale EXIT effettivo (fase finale dell'uscita)

Problemi correlati