2009-03-26 7 views
60

Je crée des fichiers temporaires à partir d'un script bash. Je les supprime à la fin du traitement, mais depuis que le script est en cours d'exécution depuis longtemps, si je le tue ou simplement CTRL-C pendant l'exécution, les fichiers temporaires ne sont pas supprimés.
Existe-t-il un moyen de capturer ces événements et de nettoyer les fichiers avant la fin de l'exécution?Suppression de fichiers temporaires créés dans une sortie bash inattendue

De même, existe-t-il une sorte de meilleure pratique pour la dénomination et l'emplacement de ces fichiers temporaires?
Je suis actuellement pas sûr entre l'utilisation:

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

et

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

Ou est peut-être at-il des meilleures solutions?

Répondre

55

Vous pouvez définir une « trap » pour exécuter la sortie ou sur un contrôle-c pour nettoyer.

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

Alternativement, un de mes unix-ismes favori est d'ouvrir un fichier, puis supprimez-le pendant que vous avez encore ouvert. Le fichier reste sur le système de fichiers et vous pouvez le lire et l'écrire, mais dès que votre programme se termine, le fichier disparaît. Je ne sais pas comment vous feriez cela à bash, cependant.

BTW: Un argument que je donnerai en faveur de mktemp au lieu d'utiliser votre propre solution: si l'utilisateur prévoit que votre programme va créer d'énormes fichiers temporaires, il voudra peut-être définir TMPDIR comme quelque chose de plus grand, comme/var/tmp mktemp reconnaît que votre solution roulée à la main (deuxième option) ne le fait pas. J'utilise fréquemment TMPDIR=/var/tmp gvim -d foo bar, par exemple.

+7

Avec Bash, 'exec 5 <> $ TMPFILE' lie le descripteur de fichier 5 à $ TMPFILE en lecture-écriture , et vous pouvez utiliser '<&5', '> & 5', et'/proc/$$/fd/5' (Linux) par la suite. Le seul problème est que Bash manque de la fonction 'seek' ... – ephemient

+0

Vous avez accepté de répondre depuis le lien que vous avez fourni est ce qui explique le mieux ce dont j'avais besoin. Merci – skinp

+3

Quelques notes sur 'trap': il n'y a aucun moyen de piéger' SIGKILL' (par la conception, car elle se termine immédiatement l'exécution). Donc, si cela peut arriver, ayez un plan de secours (tel que 'tmpreaper'). Deuxièmement, les pièges ne sont pas cumulatifs - si vous avez plus d'une action à effectuer, ils doivent tous être dans la commande 'trap'. Une façon de gérer plusieurs actions de nettoyage est de définir une fonction (et vous pouvez la redéfinir au fur et à mesure de la progression de votre programme) et de faire référence à: 'trap cleanup_function EXIT'. –

-4

Vous n'avez pas besoin d'enlever les fichiers tmp créés avec mktemp. Ils seront supprimés de toute façon plus tard.

Utilisez mktemp si vous le pouvez car cela génère plus de fichiers uniques que le préfixe '$$'. Et il semble que la façon de créer des fichiers temporaires soit plus cross-plate-forme, puis les mettre explicitement dans/tmp.

+4

Supprimé par qui ou quoi? – innaM

+0

supprimées au cours | système de fichiers lui-même après un certain temps –

+4

magique? Un cronjob? Ou une machine Solaris redémarrée? – innaM

16

Vous voulez utiliser la commande trap pour gérer la sortie du script ou des signaux comme CTRL-C. Voir le Advanced Bash Scripting Guide pour plus de détails.

Pour vos fichiers temporaires, en utilisant basename $0 est une bonne idée, ainsi que de fournir un modèle qui offre de la place pour assez les fichiers temporaires:

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

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

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

Ne pas intercepter sur TERM/INT. Piège à EXIT. Essayer de prédire l'état de sortie en fonction des signaux reçus est bête et certainement pas un fourre-tout. – lhunath

+2

Point secondaire: utilisez $() au lieu de guillemets simples. Et mettre des guillemets autour de $ 0 car il pourrait contenir des espaces. –

+0

Eh bien, les guillemets fonctionnent bien dans ce commentaire, mais c'est un bon point, c'est bien d'avoir l'habitude d'utiliser '$()'. Ajouté les guillemets doubles aussi. –

88

Je crée généralement un répertoire dans lequel placer tous mes fichiers temporaires, et immédiatement après, créez un gestionnaire EXIT pour nettoyer ce répertoire lorsque le script se termine.

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

Si vous mettez tous vos fichiers temporaires sous $MYTMPDIR, ils seront tous supprimés lors de votre script dans la sortie de la plupart des cas. Tuer un processus avec SIGKILL (kill -9) tue le processus tout de suite, donc votre gestionnaire EXIT ne fonctionnera pas dans ce cas.

+20

+ 1 Utilisez certainement un piège sur EXIT, pas stupide TERM/INT/HUP/tout ce que vous pouvez penser d'autre. Cependant, rappelez-vous de ** citer ** vos extensions de paramètres et je * vous * recommanderais * également * votre guillemets: piège 'rm -rf "$ TMPDIR"' EXIT – lhunath

+4

guillemets simples, parce que votre piège sera fonctionne encore si plus tard dans votre script, vous décidez de nettoyer et de changer TMPDIR en raison de circonstances. – lhunath

+10

Point secondaire: utilisez $() au lieu de guillemets simples. –

3

L'alternative d'utiliser un nom de fichier prévisible $$ est un trou béant de sécurité et vous ne devriez jamais, jamais, jamais pensé à l'utiliser. Même s'il ne s'agit que d'un simple script personnel sur votre PC mono-utilisateur. C'est une très mauvaise habitude que vous ne devriez pas obtenir. BugTraq est plein d'incidents "fichier temporaire non sécurisé".Voir here, here et here pour plus d'informations sur l'aspect de la sécurité des fichiers temporaires. Je pensais initialement à citer les affectations non sécurisées TMP1 et TMP2, mais à la réflexion, cela serait probablement not be a good idea.

1

Je préfère utiliser tempfile qui crée un fichier dans/tmp de la manière sûre et vous n'avez pas à vous soucier de sa dénomination:

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

tempfile est malheureusement très peu portable mais plus sûr, il est donc souvent préférable de l'éviter ou du moins de l'émuler. – lericson

3

Il suffit de garder à l'esprit que CHOISI réponse est bashism, ce qui signifie que la solution comme

trap "{ rm -f $LOCKFILE }" EXIT 

ne peut fonctionner que dans bash (il ne sera pas attraper Ctrl + c si shell est dash ou classique sh), mais si vous voulez la compatibilité alors vous avez encore besoin d'énumérer tous les signaux que vous voulez tr ap. Gardez également à l'esprit que lorsque le script se termine, le piège du signal "0" (EXIT) est toujours exécuté, ce qui entraîne une double exécution de la commande trap.

que la raison pour ne pas empiler tous les signaux dans une ligne si le signal est EXIT.

Pour mieux le comprendre regarder le script suivant qui fonctionnera à travers différents systèmes sans changements:

#!/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 

Cette solution vous donnera plus de contrôle puisque vous pouvez exécuter une partie de votre code sur la présence de signal réel juste avant exit final (preExit fonction) et si nécessaire, vous pouvez exécuter du code au signal EXIT réel (étape finale de sortie)

Questions connexes