Können wir temporäre Ordner wie temporäre Dateien verwenden?
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
Was wird nach diesem Shell-Exit automatisch zerstört?
Können wir temporäre Ordner wie temporäre Dateien verwenden?
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
Was wird nach diesem Shell-Exit automatisch zerstört?
Antworten:
Im Fall einer temporären Datei würde Ihr Beispiel in der Frage sie erstellen und dann die Verknüpfung zum Verzeichnis aufheben (wodurch sie "verschwindet"). Wenn das Skript den Dateiskriptor schließt (wahrscheinlich nach Beendigung), wird der von der Datei belegte Speicherplatz belegt würde vom System zurückgefordert werden. Dies ist eine übliche Methode, um mit temporären Dateien in Sprachen wie C umzugehen.
Soweit ich weiß, ist es nicht möglich, ein Verzeichnis auf die gleiche Weise zu öffnen, zumindest nicht auf eine Weise, die das Verzeichnis nutzbar machen würde.
Eine übliche Methode zum Löschen temporärer Dateien und Verzeichnisse beim Beenden eines Skripts ist die Installation einer Bereinigungsfalle EXIT
. Die unten angegebenen Codebeispiele vermeiden, dass Dateideskriptoren vollständig jongliert werden müssen.
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT
# The rest of the script goes here.
Oder Sie können eine Bereinigungsfunktion aufrufen:
cleanup () {
rm -f "$tmpfile"
rm -rf "$tmpdir"
}
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap cleanup EXIT
# The rest of the script goes here.
Der EXIT
Trap wird beim Empfang des KILL
Signals (das nicht abgefangen werden kann) nicht ausgeführt , was bedeutet, dass dann keine Bereinigung durchgeführt wird. Es wird jedoch ausgeführt, wenn es aufgrund eines INT
oder TERM
-Signals beendet wird (wenn es mit bash
oder ausgeführt wird ksh
, in anderen Shells möchten Sie diese Signale möglicherweise nach EXIT
der trap
Befehlszeile hinzufügen ) oder wenn es normal beendet wird, weil es am Ende des Skripts ankommt oder ein ausführt exit
Anruf.
.
und ..
Einträge. (Getestet unter Linux, ich weiß nicht, ob das plattformübergreifend konsistent ist.)
exec another-command
offensichtlich aufruft .
Schreiben Sie eine Shell-Funktion, die ausgeführt wird, wenn Ihr Skript fertig ist. Im folgenden Beispiel nenne ich es "Aufräumen" und setze einen Trap, der auf Exit-Ebenen ausgeführt werden soll, wie: 0 1 2 3 6
trap cleanup 0 1 2 3 6
cleanup()
{
[ -d $TMP ] && rm -rf $TMP
}
Weitere Informationen finden Sie in diesem Beitrag.
cleanup
vor einem sauberen Ausgang (0) und beim Empfang von SIGHUP (1), SIGINT (2), SIGQUIT (3) und SIGABRT (6). Es wird nicht ausgeführt, cleanup
wenn das Skript aufgrund von SIGTERM, SIGSEGV, SIGKILL, SIGPIPE usw. beendet wird. Dies ist eindeutig mangelhaft.
Sie können chdir hinein und dann entfernen, vorausgesetzt, Sie versuchen danach nicht, Pfade darin zu verwenden:
#! /bin/sh
dir=`mktemp -d`
cd "$dir"
exec 4>file 3<file
rm -fr "$dir"
echo yes >&4 # OK
cat <&3 # OK
cat file # FAIL
echo yes > file # FAIL
Ich habe nicht überprüft, aber es ist höchstwahrscheinlich das gleiche Problem, wenn openat (2) in C mit einem Verzeichnis verwendet wird, das nicht mehr im Dateisystem vorhanden ist.
Wenn Sie root und unter Linux sind, können Sie mit einem separaten Namespace und mount -t tmpfs tmpfs /dir
darin spielen.
Die kanonischen Antworten (setzen Sie eine Falle auf EXIT) funktionieren nicht, wenn Ihr Skript in einen unreinen Exit gezwungen wird (z. B. mit SIGKILL); Dadurch können vertrauliche Daten hängen bleiben.
Aktualisieren:
Hier ist ein kleines Dienstprogramm, das den Namespace-Ansatz implementiert. Es sollte mit kompiliert werden
cc -Wall -Os -s chtmp.c -o chtmp
und gegebene CAP_SYS_ADMIN
Dateifunktionen (als root) mit
setcap CAP_SYS_ADMIN+ep chtmp
Beim Ausführen (als normaler) Benutzer als
./chtmp command args ...
Der Dateisystem-Namespace wird freigegeben, ein tmpfs-Dateisystem wird gemountet /proc/sysvipc
, chdir wird eingefügt und command
mit den angegebenen Argumenten ausgeführt. command
wird die Fähigkeiten nicht erben CAP_SYS_ADMIN
.
Auf dieses Dateisystem kann nicht über einen anderen Prozess zugegriffen werden, der nicht gestartet wurde command
, und es verschwindet auf magische Weise (mit allen darin erstellten Dateien), wenn command
und seine Kinder sterben, egal wie das passiert. Beachten Sie, dass dies nur die Freigabe des Mount-Namespace aufhebt. Es gibt keine harten Barrieren zwischen command
und anderen Prozessen, die vom selben Benutzer ausgeführt werden. sie konnten nach wie vor in seinem Namensraum schleichen entweder über ptrace(2)
, /proc/PID/cwd
oder mit anderen Mitteln.
Die Entführung der "Nutzlosen" /proc/sysvipc
ist natürlich albern, aber die Alternative wäre gewesen, /tmp
mit leeren Verzeichnissen zu spammen , die entfernt werden müssten, oder dieses kleine Programm mit Gabeln und Wartezeiten erheblich zu komplizieren. Alternativ dir
kann z. /mnt/chtmp
und lassen Sie es bei der Installation von root erstellen; Machen Sie es nicht vom Benutzer konfigurierbar und setzen Sie es nicht auf einen benutzereigenen Pfad, da Sie dadurch möglicherweise Symlink-Fallen und anderen haarigen Dingen ausgesetzt werden, für die es sich nicht lohnt, Zeit zu investieren.
chtmp.c
#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mount.h>
int main(int argc, char **argv){
char *dir = "/proc/sysvipc"; /* LOL */
if(argc < 2 || !argv[1]) errx(1, "usage: %s prog args ...", *argv);
argv++;
if(unshare(CLONE_NEWNS)) err(1, "unshare(CLONE_NEWNS)");
/* "modern" systemd remounts all mount points MS_SHARED
see the NOTES in mount_namespaces(7); YUCK */
if(mount("none", "/", 0, MS_REC|MS_PRIVATE, 0))
err(1, "mount(/, MS_REC|MS_PRIVATE)");
if(mount("tmpfs", dir, "tmpfs", 0, 0)) err(1, "mount(tmpfs, %s)", dir);
if(chdir(dir)) err(1, "chdir %s", dir);
execvp(*argv, argv);
err(1, "execvp %s", *argv);
}
rm $PWD
Arbeit, befindet sich die Shell immer noch in diesem Verzeichnis. Es können jedoch keine neuen Dateien in diesen "Ordner" gelegt werden. Sie können nur mit den Dateien & 3, & 4 lesen / schreiben. Dies ist also immer noch "temporäre Datei", nicht "temporärer Ordner".
Benötigen Sie eine bestimmte Shell?
Wenn zsh eine Option ist, lesen Sie bitte zshexpn(1)
:
Wenn = (...) anstelle von <(...) verwendet wird, ist die als Argument übergebene Datei der Name einer temporären Datei, die die Ausgabe des Listenprozesses enthält. Dies kann anstelle des Formulars <für ein Programm verwendet werden, das erwartet
lseek
(siehelseek(2)
) in der Eingabedatei.[...]
Ein weiteres Problem tritt immer dann auf, wenn ein Job mit einer Ersetzung, für die eine temporäre Datei erforderlich ist, von der Shell abgelehnt wird, einschließlich des Falls, in dem
&!
oder&|
am Ende eines Befehls, der eine Ersetzung enthält, angezeigt wird. In diesem Fall wird die temporäre Datei nicht bereinigt, da die Shell keinen Speicher mehr für den Job hat. Eine Problemumgehung besteht darin, eine Unterschale zu verwenden, z.(mycmd =(myoutput)) &!
Da die gegabelte Subshell auf den Abschluss des Befehls wartet, wird die temporäre Datei entfernt.
Eine allgemeine Problemumgehung, um sicherzustellen, dass eine Prozessersetzung über einen angemessenen Zeitraum andauert, besteht darin, sie als Parameter an eine anonyme Shell-Funktion zu übergeben (ein Teil des Shell-Codes, der sofort mit Funktionsumfang ausgeführt wird). Zum Beispiel dieser Code:
() { print File $1: cat $1 } =(print This be the verse)
gibt etwas aus, das dem Folgenden ähnelt
File /tmp/zsh6nU0kS: This be the verse
Zum Beispiel verwende ich dies in Rifle (Teil des Ranger-Dateimanagers), um eine Datei zu entschlüsseln und dann das Gewehr für die temporäre Datei auszuführen, die gelöscht wird, wenn die Unterprozesse beendet werden. (nicht vergessen zu setzen $TERMCMD
)
# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")