Czy możemy używać folderów tymczasowych, takich jak pliki tymczasowe
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
które zostaną zniszczone automatycznie po wyjściu z tej powłoki?
Czy możemy używać folderów tymczasowych, takich jak pliki tymczasowe
TMP=$(mktemp ... )
exec 3<>$TMP
rm $TMP
cat <&3
które zostaną zniszczone automatycznie po wyjściu z tej powłoki?
Odpowiedzi:
W przypadku pliku tymczasowego twój przykład w pytaniu utworzy go, a następnie odłączy go od katalogu (sprawiając, że „zniknie”), a gdy skrypt zamknie deskryptor plików (prawdopodobnie po zakończeniu), miejsce zajmowane przez plik byłby możliwy do odzyskania przez system. Jest to powszechny sposób postępowania z plikami tymczasowymi w językach takich jak C.
O ile mi wiadomo, nie można otworzyć katalogu w ten sam sposób, a przynajmniej nie w żaden sposób, który uczyniłby katalog użytecznym.
Częstym sposobem usuwania plików tymczasowych i katalogów po zakończeniu skryptu jest zainstalowanie EXIT
pułapki czyszczenia . Poniższe przykłady kodu pozwalają uniknąć konieczności żonglowania deskryptorami plików.
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"; rm -rf "$tmpdir"' EXIT
# The rest of the script goes here.
Lub możesz wywołać funkcję czyszczenia:
cleanup () {
rm -f "$tmpfile"
rm -rf "$tmpdir"
}
tmpdir=$(mktemp -d)
tmpfile=$(mktemp)
trap cleanup EXIT
# The rest of the script goes here.
EXIT
Pułapka nie zostanie wykonana po otrzymaniu KILL
sygnału (który nie może być uwięziony), co oznacza, że nie będzie żadnego czyszczenia wykonywane wtedy. Będzie on jednak wykonywany po zakończeniu z powodu sygnału INT
lub TERM
(jeśli działa z bash
lub ksh
w innych powłokach, możesz chcieć dodać te sygnały po EXIT
w trap
wierszu poleceń), lub gdy kończy się normalnie z powodu dojścia do końca skryptu lub wykonania exit
połączenie.
.
i ..
. (Testowany w systemie Linux, nie wiem, czy jest spójny na różnych platformach).
exec another-command
oczywiście.
Napisz funkcję powłoki, która zostanie wykonana po zakończeniu skryptu. W poniższym przykładzie nazywam to „porządkiem” i ustawiam pułapkę do wykonania na poziomach wyjścia, np .: 0 1 2 3 6
trap cleanup 0 1 2 3 6
cleanup()
{
[ -d $TMP ] && rm -rf $TMP
}
Zobacz ten post, aby uzyskać więcej informacji.
cleanup
przed czystym wyjściem (0) i po otrzymaniu SIGHUP (1), SIGINT (2), SIGQUIT (3) i SIGABRT (6). będzie nie działać cleanup
, gdy wychodzi script powodu SIGTERM, SIGSEGV, SIGKILL, SIGPIPE itp Jest to wyraźnie niewystarczające.
Możesz do niego przejść, a następnie go usunąć, pod warunkiem, że później nie będziesz próbował używać ścieżek:
#! /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
Nie sprawdziłem, ale najprawdopodobniej ten sam problem występuje podczas używania openat (2) w C z katalogiem, który już nie istnieje w systemie plików.
Jeśli jesteś rootem i na Linuksie, możesz grać z oddzielną przestrzenią nazw i mount -t tmpfs tmpfs /dir
wewnątrz niej.
Odpowiedzi kanoniczne (ustaw pułapkę na EXIT) nie działają, jeśli twój skrypt jest zmuszony do nieczystego wyjścia (np. Za pomocą SIGKILL); które mogą pozostawić wrażliwe dane.
Aktualizacja:
Oto małe narzędzie, które implementuje podejście do przestrzeni nazw. Należy go skompilować
cc -Wall -Os -s chtmp.c -o chtmp
i dane CAP_SYS_ADMIN
możliwości pliku (jako root) za pomocą
setcap CAP_SYS_ADMIN+ep chtmp
Po uruchomieniu (jako normalny) użytkownik jako
./chtmp command args ...
usunie udostępnienie przestrzeni nazw systemu plików, zamontuje system plików tmpfs /proc/sysvipc
, chdir i uruchomi command
podane argumenty. command
będzie nie dziedziczą CAP_SYS_ADMIN
możliwości.
Ten system plików nie będzie dostępny z innego procesu, od którego nie został uruchomiony command
, i magicznie zniknie (wraz ze wszystkimi plikami, które zostały w nim utworzone), kiedy command
i jego dzieci umrą, bez względu na to, jak to się stanie. Zauważ, że jest to po prostu udostępnianie przestrzeni nazw montowania - nie ma twardych barier między command
innymi procesami uruchamianymi przez tego samego użytkownika; wciąż mogli się zakraść w przestrzeni nazw albo przez ptrace(2)
, /proc/PID/cwd
albo w inny sposób.
Porwanie „bezużytecznego” /proc/sysvipc
jest oczywiście głupie, ale alternatywą byłoby spamowanie /tmp
pustymi katalogami, które należałoby usunąć lub znacznie komplikować ten mały program widelcami i czekaniem. Alternatywnie dir
można zmienić na np. /mnt/chtmp
i niech utworzy go root podczas instalacji; nie konfiguruj go i nie ustawiaj na ścieżkę należącą do użytkownika, ponieważ może to narazić Cię na pułapki dowiązań symbolicznych i inne włochate rzeczy, na których nie warto spędzać czasu.
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
praca, skorupa wciąż jest w tym katalogu. Ale w tym „folderze” nie można umieszczać nowych plików. Jedyne, co możesz zrobić, to odczyt / zapis za pomocą plików i 3 oraz 4. Jest to więc nadal „plik tymczasowy”, a nie „folder tymczasowy”.
Czy potrzebujesz konkretnej powłoki?
Jeśli zsh jest opcją, przeczytaj zshexpn(1)
:
Jeśli zamiast <(...) zostanie użyta wartość = (...), plik przekazany jako argument będzie nazwą pliku tymczasowego zawierającego dane wyjściowe procesu listy. Można tego użyć zamiast formularza <dla programu, który oczekuje
lseek
(patrzlseek(2)
) w pliku wejściowym.[...]
Kolejny problem pojawia się za każdym razem, gdy powłoka odrzuca zadanie z podstawieniem, które wymaga pliku tymczasowego, w tym przypadek, w którym
&!
lub&|
pojawia się na końcu polecenia zawierającego podstawienie. W takim przypadku plik tymczasowy nie zostanie wyczyszczony, ponieważ powłoka nie ma już pamięci zadania. Obejściem tego problemu jest użycie podpowłoki, na przykład(mycmd =(myoutput)) &!
ponieważ rozwidlona podpowłoka będzie czekać na zakończenie polecenia, a następnie usunie plik tymczasowy.
Ogólnym obejściem zapewniającym, że podstawienie procesu trwa przez odpowiedni czas, jest przekazanie go jako parametru do anonimowej funkcji powłoki (fragment kodu powłoki uruchamiany natychmiast z zakresem funkcji). Na przykład ten kod:
() { print File $1: cat $1 } =(print This be the verse)
wyprowadza coś podobnego do następującego
File /tmp/zsh6nU0kS: This be the verse
Na przykład używam tego w karabinie (część menedżera plików ranger), aby odszyfrować plik, a następnie uruchomić karabin na pliku tymczasowym, który zostanie usunięty po zakończeniu podprocesów. (nie zapomnij ustawić $TERMCMD
)
# ~/.config/ranger/rifle.conf
...
!ext exe, mime octet-stream$, has gpg, flag t = () { rifle -f F "$1" } =(gpg -dq "$1")