Wie erstelle ich eine temporäre Datei in einem Shell-Skript?

155

Beim Ausführen eines Skripts möchte ich eine temporäre Datei im /tmpVerzeichnis erstellen .

Nach der Ausführung dieses Skripts wird dieses Skript bereinigt.

Wie mache ich das in Shell-Skript?

Bhuvanesh
quelle

Antworten:

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

Sie können sicherstellen, dass eine Datei gelöscht wird, wenn das Skript beendet wird (einschließlich Abbrüchen und Abstürzen), indem Sie einen Dateideskriptor für die Datei öffnen und diese löschen. Die Datei bleibt verfügbar (für das Skript; nicht wirklich für andere Prozesse, /proc/$PID/fd/$FDist aber ein Workaround), solange der Dateideskriptor geöffnet ist. Wenn es geschlossen wird (was der Kernel automatisch tut, wenn der Prozess beendet wird), löscht das Dateisystem die Datei.

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3
Hauke ​​Laging
quelle
4
Gute Antwort, elegante Lösung mit dem Dateideskriptor im Falle eines Absturzes +1
Chaos
2
/proc- außer für Systeme, die es nicht haben.
Dennis Williamson
4
Was bedeutet das exec 3> "$tmpfile"tun? Ist das nicht nur dann sinnvoll, wenn es sich bei der tmpfile um ein eigenständiges Skript handelt?
Alexej Magura
5
Wie liest man aus der erstellten FD?
Eckes
3
"Sie können Katze <3 oder ähnliches verwenden." eigentlich liest das aus einer datei namens 3 @ dragon788. Auch cat <&3wird geben Bad file descriptor. Ich würde es begrüßen, wenn Sie es entweder reparieren oder entfernen; Fehlinformationen helfen nicht viel.
Daniel Farrell
65

Verwenden Sie mktempdiese Option , um eine temporäre Datei oder ein temporäres Verzeichnis zu erstellen:

temp_file=$(mktemp)

Oder für ein Direcotry:

temp_dir=$(mktemp -d)

Am Ende des Skripts müssen Sie die temporäre Datei / dir löschen:

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

mktemp erstellt eine Datei im /tmpVerzeichnis oder in der mit dem --tmpdirArgument angegebenen Verzeichnisstruktur .

Chaos
quelle
20
Sie können trap "rm -f $temp_file" 0 2 3 15direkt nach dem Erstellen der Datei verwenden, so dass beim Beenden des Skripts oder beim Stoppen ctrl-Cdie Datei weiterhin entfernt wird.
Wurtel
1
@wurtel Was passiert, wenn EXITder einzige Haken dafür ist trap?
Hauke ​​Laging
4
@HaukeLaging Dann wird die Falle nicht ausgelöst, wenn das Skript mit Strg + C gestoppt wird. Eine Sache zu beachten ist, dass TRAP nicht hilft, wenn Sie kill -9 $somepid. Dieses besondere Kill-Signal ist Insta-Death, und nichts anderes passiert.
Dragon788
5
@ dragon788 Hast du das versucht? Du solltest. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging
Das Fangen EXITist genug.
Kusalananda
15

Wenn Sie sich auf einem System mit mktemp befinden , sollten Sie es als andere Antwort verwenden.

Mit POSIX-Werkzeugkasten:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"
cuonglm
quelle
Was passiert, wenn EXITder einzige Haken für ist trap?
Hauke ​​Laging
@HaukeLaging: Wird tmpfilevor dem Beenden des Skripts noch entfernt, aber nicht, wenn das Skript andere Signale empfangen hat.
Montag,
Genau das passiert hier nicht (GNU bash, Version 4.2.53).
Hauke ​​Laging
@HaukeLaging: Was meinst du That's not what happens?
Sonntag,
3
mktempentstanden in HP / UX mit einer anderen Syntax. Todd C. Miller hat Mitte der 90er Jahre eine andere Version für OpenBSD entwickelt (kopiert von FreeBSD und NetBSD) und später auch als eigenständiges Hilfsprogramm zur Verfügung gestellt (www.mktemp.org). Dies war diejenige, die normalerweise unter Linux verwendet wurde, bis 2007 ein (größtenteils kompatibles) mktempDienstprogramm zu den GNU-Coreutils hinzugefügt wurde mktemp.
Stéphane Chazelas
14

Einige Shells haben die Funktion eingebaut.

zsh

zshDie =(...)Form der Prozessersetzung verwendet eine temporäre Datei. Erweitert beispielsweise =(echo test)den Pfad einer temporären Datei, die enthält 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

Diese Datei wird nach Beendigung des Befehls automatisch entfernt.

bash / zsh unter Linux.

Here-Dateien oder Here-Strings in bashund zshwerden als gelöschte temporäre Dateien implementiert.

Also wenn du tust:

exec 3<<< test

Der Dateideskriptor 3 ist mit einer gelöschten temporären Datei verbunden, die enthält test\n.

Sie können den Inhalt erhalten mit:

cat <&3

Unter Linux können Sie diese Datei auch über lesen oder schreiben /dev/fd/3

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

(Einige andere Shells verwenden Pipes oder können verwendet werden, /dev/nullwenn das Dokument hier leer ist.)

POSIX

Es gibt kein mktempPOSIX-Dienstprogramm. POSIX gibt jedoch eine mkstemp(template)C-API an , und das m4Standarddienstprogramm macht diese API mit der gleichnamigen mkstemp()m4-Funktion verfügbar.

mkstemp()gibt Ihnen einen Dateinamen mit einem zufälligen Teil, der zum Zeitpunkt des Funktionsaufrufs garantiert nicht vorhanden war. Es erstellt die Datei mit den Berechtigungen 0600 auf rennfreie Weise.

Sie könnten also Folgendes tun:

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

Beachten Sie jedoch, dass Sie die Bereinigung beim Beenden durchführen müssen. Wenn Sie die Datei jedoch nur eine feste Anzahl von Malen schreiben und lesen müssen, können Sie sie direkt nach dem Erstellen öffnen und löschen, z. B. für here-doc / here-. String-Ansatz oben:

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

Sie können die Datei zum Lesen einmal öffnen und zwischen zwei Lesevorgängen zurückspulen. Es gibt jedoch kein POSIX-Dienstprogramm, das das Zurückspulen durchführen kann ( lseek()). Sie können es also nicht portabel in einem POSIX-Skript ( zsh( sysseekeingebaut) und ksh93( <#((...))Operator) ausführen mach es aber).

Stéphane Chazelas
quelle
1
Bash hat auch Prozess Substitution mit<()
WinnieNicklaus
3
@ WinnieNicklaus, ja, aber das verwendet keine temporären Dateien, ist hier irrelevant. Prozess Substitution wurde durch KSH, kopiert von bash und zsh eingeführt und zsh erweiterte es mit einer 3.en Form: =(...).
Stéphane Chazelas
7

Hier ist eine etwas verbesserte Antwort in der Zeile von 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
Schwäne
quelle
2
Es ist zu beachten, dass der Inhalt nur einmal verfügbar ist. Dh wenn ich zum zweiten Mal cat <& $ FD_R mache, wird keine Ausgabe erzeugt. Siehe unix.stackexchange.com/questions/166482/… . Gibt es eine Möglichkeit, die Datei automatisch zu löschen, wenn das Programm abstürzt, aber mehrmals darauf zugreifen zu können?
Smihael
0

Mein Workflow mit temporären Dateien basiert normalerweise auf einem Bash-Skript, das ich teste. Ich möchte teees öffnen, damit ich sehen kann, dass es funktioniert, und die Ausgabe für die nächste Iteration meines Prozesses speichern. Ich habe eine Datei namens erstellttmp

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

damit ich es gerne nutzen kann

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

Der Grund, warum ich die vor den Zufallswerten formatierte Datumszeit mag, ist, dass ich die soeben erstellte tmp-Datei leicht finden kann und nicht darüber nachdenken muss, wie ich sie das nächste Mal benennen soll (und mich darauf konzentrieren muss, nur mein Dang-Skript abzurufen arbeiten).

Frank Bryce
quelle