Come creare un file temporaneo nello script della shell?


155

Durante l'esecuzione di uno script, voglio creare un file temporaneo nella /tmpdirectory.

Dopo l'esecuzione di quello script, questo verrà pulito da quello script.

Come farlo nello script della shell?

Risposte:


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

Puoi assicurarti che un file venga eliminato quando escono gli script (inclusi uccisioni e arresti anomali) aprendo un descrittore di file al file ed eliminandolo. Il file rimane disponibile (per lo script; non proprio per altri processi ma /proc/$PID/fd/$FDè una soluzione) finché il descrittore di file è aperto. Quando viene chiuso (cosa che il kernel fa automaticamente all'uscita dal processo) il filesystem cancella il file.

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3

4
Buona risposta, soluzione elegante con il descrittore di file in caso di crash +1
caos

2
/proc- ad eccezione dei sistemi che non lo hanno.
Dennis Williamson,

4
cosa fa exec 3> "$tmpfile"? Non è utile solo se il file tmp è uno script autonomo?
Alexej Magura,

5
Come leggi dall'FD creato?
Verifica il

3
"Puoi usare cat <3 o qualcosa di simile." in realtà legge da un file chiamato 3 @ dragon788. Inoltre, cat <&3darà Bad file descriptor. Lo apprezzerei se lo riparassi o lo rimuovessi; la disinformazione non aiuta molto.
Daniel Farrell,

65

Utilizzare mktempper creare un file o una directory temporaneo:

temp_file=$(mktemp)

O per una regia:

temp_dir=$(mktemp -d)

Alla fine dello script devi eliminare il file / dir temporaneo:

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

mktemp crea il file nella /tmpdirectory o nella directory indicata con l' --tmpdirargomento.


20
È possibile utilizzare trap "rm -f $temp_file" 0 2 3 15subito dopo aver creato il file in modo che quando lo script viene chiuso o interrotto con ctrl-Cil file sia ancora rimosso.
wurtel,

1
@wurtel Cosa succede se EXITè l'unico hook per trap?
Hauke ​​Laging,

4
@HaukeLaging Quindi la trap non si attiva se lo script viene arrestato con Ctrl + C. Una cosa da notare è che TRAP non aiuta se tu kill -9 $somepid. Quel particolare segnale di uccisione è insta-morte con nient'altro che accade.
dragon788,

5
@ dragon788 Ci hai provato? Dovresti. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging,

Il trapping EXITè abbastanza.
Kusalananda

15

Se sei su un sistema che ha mktemp , dovresti usarlo come altre risposte.

Con il toolchest POSIX:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"

Cosa succede se EXITè l'unico gancio per trap?
Hauke ​​Laging,

@HaukeLaging: tmpfileessere comunque rimosso prima dell'uscita dello script, ma non quando lo script ha ricevuto altri segnali.
martedì

Non è quello che succede qui (GNU bash, versione 4.2.53).
Hauke ​​Laging,

@HaukeLaging: cosa intendi That's not what happens?
cuonglm,

3
mktemporiginato in HP / UX con una sintassi diversa. Todd C. Miller ne creò uno diverso per OpenBSD a metà degli anni '90 (copiato da FreeBSD e NetBSD) e in seguito lo rese disponibile anche come utility standalone (www.mktemp.org). Questo è quello che è stato generalmente utilizzato su Linux fino a quando mktempun'utilità (per lo più compatibile) è stata aggiunta ai coreutils GNU nel 2007. Solo per dire che non si può davvero dire che mktempè un'utilità GNU.
Stéphane Chazelas,

14

Alcune shell hanno la funzione integrata.

zsh

zshLa =(...)forma di sostituzione del processo utilizza un file temporaneo. Ad esempio, si =(echo test)espande nel percorso di un file temporaneo che contiene 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

Tale file viene automaticamente rimosso, al termine del comando.

bash / zsh su Linux.

Here-files o here-string in bashe zshvengono implementati come file temporanei eliminati.

Quindi se lo fai:

exec 3<<< test

Il descrittore di file 3 è collegato a un file temporaneo eliminato che contiene test\n.

Puoi ottenere il suo contenuto con:

cat <&3

Se su Linux, puoi anche leggere o scrivere su quel file tramite /dev/fd/3

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

(alcune altre shell usano pipe o possono usare /dev/nullse il documento qui è vuoto).

POSIX

Non esiste alcuna mktemputilità POSIX. POSIX specifica tuttavia mkstemp(template)un'API C e l' m4utilità standard espone tale API con la mkstemp()funzione m4 con lo stesso nome.

mkstemp()ti dà un nome di file con una parte casuale che era garantita non esistere al momento della chiamata della funzione. Crea il file con i permessi 0600 in un modo senza gara.

Quindi, potresti fare:

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

Si noti tuttavia che è necessario gestire la pulizia all'uscita, anche se se si deve solo scrivere e leggere il file un numero fisso di volte, è possibile aprirlo ed eliminarlo subito dopo aver creato come per here-doc / here- approccio stringa sopra:

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

È possibile aprire il file per la lettura una volta e riavvolgere tra due letture, tuttavia non esiste un'utilità POSIX in grado di eseguire tale riavvolgimento ( lseek()), quindi non è possibile farlo in uno script POSIX ( zsh( sysseekincorporato) e ksh93( <#((...))operatore) possono fallo però).


1
Bash ha anche la sostituzione del processo usando<()
WinnieNicklaus il

3
@WinnieNicklaus, sì, ma che non utilizza file temporanei, quindi è irrilevante qui. La sostituzione di processo è stato introdotto da ksh, copiato da bash e zsh, e zsh ampliato con un 3 ° modulo: =(...).
Stéphane Chazelas,

7

Ecco una risposta leggermente migliorata nella linea di 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

2
Va notato che il contenuto è disponibile solo una volta. Vale a dire se faccio cat <& $ FD_R per la seconda volta, non viene prodotto alcun output. Vedi unix.stackexchange.com/questions/166482/… . Esiste un modo per eliminare automaticamente il file in caso di arresto anomalo del programma, ma rendendolo accessibile più volte?
smihael,

0

Il mio flusso di lavoro in genere con i file temporanei è a causa di alcuni script bash che sto testando. Lo voglio in teemodo da poter vedere che funziona e salvare l'output per la prossima iterazione del mio processo. Ho creato un file chiamatotmp

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

in modo che io possa usarlo come

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

Il motivo per cui mi piace il datetime formattato prima dei valori casuali è che mi permette di trovare il file tmp che ho appena creato facilmente e non devo pensare a come nominarlo la prossima volta (e concentrarmi solo su come ottenere il mio script dang lavorare).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.