Comment créer un fichier temporaire en script shell?

155

Lors de l'exécution d'un script, je souhaite créer un fichier temporaire dans un /tmprépertoire.

Après exécution de ce script, il sera nettoyé par ce script.

Comment faire cela en script shell?

Bhuvanesh
la source

Réponses:

198
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
: ...
rm "$tmpfile"

Vous pouvez vous assurer qu'un fichier est supprimé lorsque les scripts se terminent (y compris les tentatives de suppression et les crashs) en ouvrant un descripteur de fichier dans le fichier et en le supprimant. Le fichier reste disponible (pour le script; pas vraiment pour d'autres processus, mais /proc/$PID/fd/$FDconstitue une solution de contournement) tant que le descripteur de fichier est ouvert. Lorsqu'il est fermé (ce que fait automatiquement le noyau lorsque le processus se termine), le système de fichiers supprime le fichier.

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3
Hauke ​​Laging
la source
4
Bonne réponse, solution élégante avec le descripteur de fichier en cas d'accident +1
chaos
2
/proc- sauf pour les systèmes qui ne l'ont pas.
Dennis Williamson
4
qu'est-ce que le exec 3> "$tmpfile"fait? N'est-ce pas seulement utile si le fichier tmp est un script autonome?
Alexej Magura
5
Comment lisez-vous à partir du FD créé?
eckes
3
"Vous pouvez utiliser le chat <3 ou quelque chose de similaire." en fait, il lit un fichier nommé 3 @ dragon788. Aussi, cat <&3va donner Bad file descriptor. J'apprécierais si vous le répariez ou le retiriez; la désinformation n'aide pas beaucoup.
Daniel Farrell
65

Utilisez mktemppour créer un fichier temporaire ou un répertoire:

temp_file=$(mktemp)

Ou pour un répertoire:

temp_dir=$(mktemp -d)

A la fin du script, vous devez supprimer le fichier / répertoire temporaire:

rm ${temp_file}
rm -R ${temp_dir}

mktemp crée un fichier dans le /tmprépertoire ou dans le répertoire donné avec l' --tmpdirargument.

le chaos
la source
20
Vous pouvez utiliser trap "rm -f $temp_file" 0 2 3 15immédiatement après la création du fichier, de sorte que ctrl-Cle fichier soit toujours supprimé lorsque le script se ferme ou s’arrête .
Wurtel
1
@wurtel Que se passe-t-il si EXITc'est le seul crochet pour trap?
Hauke ​​Laging
4
@HaukeLaging Ensuite, l'interruption ne se déclenche pas si le script est arrêté avec Ctrl + C. Une chose à noter est que TRAP ne vous aide pas si vous kill -9 $somepid. Ce signal de destruction est une mort imminente et il ne se passe rien d'autre.
dragon788
5
@ dragon788 Avez-vous essayé cela? Vous devriez. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging
Le piégeage EXITsuffit.
Kusalananda
15

Si vous utilisez un système qui contient mktemp , vous devriez l’utiliser comme autre réponse.

Avec les outils POSIX:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"
cuonglm
la source
Qu'est-ce qui se passe si EXITest le seul crochet pour trap?
Hauke ​​Laging
@HaukeLaging: tmpfilereste supprimé avant la sortie du script, mais pas lorsque le script reçoit d'autres signaux.
jeudi
Ce n'est pas ce qui se passe ici (GNU bash, version 4.2.53).
Hauke ​​Laging le
@HaukeLaging: Que voulez-vous dire That's not what happens?
jeudi
3
mktempprovient de HP / UX avec une syntaxe différente. Todd C. Miller en a créé un différent pour OpenBSD au milieu des années 90 (copié par FreeBSD et NetBSD) et l'a ensuite rendu disponible en tant qu'utilitaire autonome (www.mktemp.org). C'est celui qui était généralement utilisé sous Linux jusqu'à ce qu'un mktemputilitaire (essentiellement compatible) soit ajouté à la coreutils de GNU en 2007. Juste pour dire, on ne peut pas vraiment dire qu'il mktemps'agit d'un utilitaire GNU.
Stéphane Chazelas
14

Certains coques ont la fonctionnalité intégrée.

zsh

zshLa =(...)forme de substitution de processus utilise un fichier temporaire. Par exemple, =(echo test)étend le chemin du fichier temporaire qui contient test\n.

$ {cat $file; ls -l /dev/fd/3; echo test2 >&3; cat $file} 3<> ${file::==(echo test)}
test
lrwx------ 1 stephane stephane 64 Jan 30 11:19 /dev/fd/3 -> /tmp/zshMLbER0
test2

Ce fichier est automatiquement supprimé une fois la commande terminée.

bash / zsh sous Linux.

Here-files ou here-strings in bashet zshsont implémentés en tant que fichiers temporaires supprimés.

Donc si vous le faites:

exec 3<<< test

Le descripteur de fichier 3 est connecté à un fichier temporaire supprimé qui contient test\n.

Vous pouvez obtenir son contenu avec:

cat <&3

Si vous êtes sous Linux, vous pouvez également lire ou écrire dans ce fichier via /dev/fd/3

$ exec 3<<< test
$ cat <&3
test
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo

(Certains autres shells utilisent des pipes, ou peuvent utiliser /dev/nullsi la doc est vide).

POSIX

Il n'y a pas d' mktemputilitaire POSIX. Cependant, POSIX spécifie une mkstemp(template)API C et l' m4utilitaire standard expose cette API avec la mkstemp()fonction m4 du même nom.

mkstemp()vous donne un nom de fichier avec une partie aléatoire dont il était garanti qu'elle n'existerait pas au moment où la fonction a été appelée. Il crée le fichier avec les autorisations 0600 sans aucune course.

Donc, vous pourriez faire:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

Notez toutefois que vous devez gérer le nettoyage à la sortie. Toutefois, si vous ne devez écrire et lire le fichier qu'un nombre fixe de fois, vous pouvez l'ouvrir et le supprimer juste après sa création, comme pour le fichier here-doc / here- approche de chaîne ci-dessus:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

# open once for writing, twice for reading:
exec 3> "$tempfile" 4< "$tempfile" 5< "$tempfile"

rm -f -- "$tmpfile"

cmd >&3   # store something in the temp file
exec 3>&- # fd no longer needed

# read the content twice:
cat <&4
cat <&5

Vous pouvez ouvrir le fichier pour lecture une fois et lseek()revenir en arrière entre deux lectures. Cependant, aucun utilitaire POSIX ne peut effectuer ce rembobinage ( ), vous ne pouvez donc pas le faire de manière portable dans un script POSIX ( zsh( sysseekintégré) et ksh93( <#((...))opérateur)). faites le cependant).

Stéphane Chazelas
la source
1
Bash a également un processus de substitution utilisant<()
WinnieNicklaus le
3
@WinnieNicklaus, oui, mais cela n'utilise pas de fichiers temporaires, ce n'est donc pas pertinent ici. La substitution de processus a été introduit par ksh, copié par bash et zsh, et zsh a étendu avec une 3ème forme: =(...).
Stéphane Chazelas
7

Voici une réponse légèrement améliorée dans la lignée de Hauke ​​Laging:

#!/bin/bash

tmpfile=$(mktemp)  # Create a temporal file in the default temporal folder of the system

# Lets do some magic for the tmpfile to be removed when this script ends, even if it crashes
exec {FD_W}>"$tmpfile"  # Create file descriptor for writing, using first number available
exec {FD_R}<"$tmpfile"  # Create file descriptor for reading, using first number available
rm "$tmpfile"  # Delete the file, but file descriptors keep available for this script

# Now it is possible to work with the temporal file
echo foo >&$FD_W
echo bar >&$FD_W  # Note that file descriptor always concatenates, not overwrites

cat <&$FD_R
Cygnes
la source
2
Il convient de noter que le contenu est disponible une seule fois. C'est-à-dire que si je cat <& $ FD_R pour la deuxième fois, aucune sortie n'est produite. Voir unix.stackexchange.com/questions/166482/… . Existe-t-il un moyen de supprimer automatiquement le fichier si le programme se bloque, mais en le rendant accessible plusieurs fois?
smihael
0

Mon flux de travail généralement avec des fichiers temporaires est dû à un script bash que je teste. Je veux que tout soit clair pour teeque je puisse voir que tout fonctionne et enregistrer la sortie pour la prochaine itération de mon processus. J'ai créé un fichier appelétmp

#!/bin/bash
echo $(mktemp /tmp/$(date +"%Y-%m-%d_%T_XXXXXX"))

afin que je puisse l'utiliser comme

$ some_command --with --lots --of --stuff | tee $(tmp)

La raison pour laquelle j’aime le format de date et d’heure précédant les valeurs aléatoires, c’est que cela me permet de trouver facilement le fichier tmp que je viens de créer, et je n’ai pas à penser à comment le nommer la prochaine fois (et à me concentrer uniquement sur mon script dang travailler).

Frank Bryce
la source