Tworzenie plików tymczasowych w bash

150

Czy istnieją obiektywnie lepsze sposoby tworzenia plików tymczasowych w skryptach bash?

Zwykle nadaję im nazwę, cokolwiek przyjdzie mi do głowy, na przykład plik tymczasowy-123, ponieważ zostanie usunięty po zakończeniu skryptu. Czy jest to jakaś wada inna niż zastąpienie możliwego pliku tymczasowego-123 w bieżącym folderze? A może jest jakaś korzyść z ostrożniejszego tworzenia pliku tymczasowego?

Strapakowsky
źródło
1
Nie używaj plików tymczasowych. Zamiast tego użyj tymczasowo katalogów. I nie używaj mktemp. Zobacz, dlaczego: codeproject.com/Articles/15956/ ...
ceving
17
@ceving Ten artykuł jest po prostu błędny, przynajmniej w przypadku zastosowania do polecenia powłoki mktemp (w przeciwieństwie do wywołania biblioteki mktemp). Ponieważ mktemp sam tworzy plik z restrykcyjną umaską, dany atak działa tylko wtedy, gdy atakujący działa na tym samym koncie co atakujący ... w takim przypadku gra jest już przegrana. Aby zapoznać się z najlepszymi praktykami w świecie skryptów powłoki, zobacz mywiki.wooledge.org/BashFAQ/062
Charles Duffy
Możesz również używać tempfile(1)w systemach, które go mają.
Wstrzymano do odwołania.

Odpowiedzi:

179

mktemp(1)Strona podręcznika opisuje go dość dobrze:

Tradycyjnie wiele skryptów powłoki przyjmuje nazwę programu z pid jako sufiks i używa go jako tymczasowej nazwy pliku. Ten rodzaj nazewnictwa jest przewidywalny, a stan wyścigu, który tworzy, jest łatwy do wygrania przez atakującego. Bezpieczniejszym, choć wciąż gorszym podejściem jest utworzenie katalogu tymczasowego przy użyciu tego samego schematu nazewnictwa. Chociaż pozwala to zagwarantować, że plik tymczasowy nie zostanie zniszczony, nadal umożliwia prosty atak typu „odmowa usługi”. Z tego powodu zaleca się użycie zamiast niego mktemp.

W skrypcie wywołuję mktemp w stylu

mydir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename $0).XXXXXXXXXXXX")

który tworzy katalog tymczasowy, w którym mogę pracować, iw którym mogę bezpiecznie nazwać rzeczywiste pliki jako czytelne i przydatne.

mktempnie jest standardem, ale istnieje na wielu platformach. „X” na ogół zostanie zamienione na pewną przypadkowość, a więcej będzie prawdopodobnie bardziej losowych; jednak niektóre systemy (na przykład popiół busybox) ograniczają tę losowość bardziej niż inne


Nawiasem mówiąc, bezpieczne tworzenie plików tymczasowych jest ważne nie tylko przy tworzeniu skryptów powłoki. Dlatego python ma plik tymczasowy , perl ma File :: Temp , ruby ​​ma plik tymczasowy itp.

kojiro
źródło
1
Opcja -t jest przestarzała: gnu.org/software/coreutils/manual/html_node/…
Tibor Vass
7
Wydaje się, że najbezpieczniejszym i najbardziej wieloplatformowym sposobem użycia mktempjest połączenie z basename, jak na przykład mktemp -dt "$(basename $0). XXXXXXXXXX". Jeśli zostanie użyty bez basenameciebie, może pojawić się błąd taki jak ten mktemp: nieprawidłowy szablon, `/tmp/MOB-SAN-JOB1-183-ScriptBuildTask-7300464891856663368.sh.XXXXXXXXXX ', zawiera separator katalogu .
i4niac
7
Zignoruj ​​literówkę (dodatkowa spacja). mktemp -dt "$(basename $0).XXXXXXXXXX"jest właściwą drogą.
i4niac
3
@ i4niac: trzeba zacytować $0, w krainie OS X jest mnóstwo miejsc.mktemp -dt "$(basename "$0").XXXXXX"
Orwellophile
11
Również fajnie może być usunięcie katalogu tempdir pod koniec wykonywania skryptu:trap "rm -rf $mydir" EXIT
KumZ
43

Tak, użyj mktemp .

Utworzy plik tymczasowy w folderze przeznaczonym do przechowywania plików tymczasowych i zagwarantuje unikalną nazwę. Wyświetla nazwę tego pliku:

> mktemp
/tmp/tmp.xx4mM3ePQY
>
Paweł
źródło
19

Możesz chcieć spojrzeć mktemp

Narzędzie mktemp bierze podany szablon nazwy pliku i nadpisuje jego część, aby utworzyć unikalną nazwę pliku. Szablon może być dowolną nazwą pliku z dołączoną liczbą „X”, na przykład /tmp/tfile.XXXXXXXXXX. Końcowe „X” są zastępowane kombinacją bieżącego numeru procesu i losowych liter.

Więcej informacji: man mktemp

John Lawrence
źródło
11

Czy jest jakaś korzyść z ostrożniejszego tworzenia pliku tymczasowego?

Pliki tymczasowe są zwykle tworzone w katalogu tymczasowym (takim jak /tmp), w którym wszyscy inni użytkownicy i procesy mają dostęp do odczytu i zapisu (każdy inny skrypt może tam tworzyć nowe pliki). Dlatego skrypt powinien uważać przy tworzeniu plików, takich jak używanie z odpowiednimi uprawnieniami (np. Tylko do odczytu dla właściciela, zobacz help umask:), a nazwa pliku nie powinna być łatwa do odgadnięcia (najlepiej losowa). W przeciwnym razie, jeśli nazwy plików nie są unikalne, może to spowodować konflikt z tym samym skryptem uruchamianym wiele razy (np. Sytuacja wyścigu) lub osoba atakująca może albo przejąć niektóre poufne informacje (np. gdy uprawnienia są zbyt otwarte, a nazwa pliku jest łatwa do odgadnięcia) lub utworzyć / zamienić plik na własną wersję kodu (np. zastąpienie poleceń lub zapytań SQL w zależności od tego, co jest przechowywane).


Możesz użyć następującego podejścia, aby utworzyć katalog tymczasowy:

TMPDIR=".${0##*/}-$$" && mkdir -v "$TMPDIR"

lub plik tymczasowy:

TMPFILE=".${0##*/}-$$" && touch "$TMPFILE"

Jednak nadal jest przewidywalny i nie jest uważany za bezpieczny.

Jak na man mktempto możemy przeczytać:

Tradycyjnie wiele skryptów powłoki przyjmuje nazwę programu z pid jako sufiks i używa go jako tymczasowej nazwy pliku. Ten rodzaj nazewnictwa jest przewidywalny, a stan wyścigu, który tworzy, jest łatwy do wygrania przez atakującego.

Dlatego dla bezpieczeństwa zaleca się użycie mktemppolecenia do utworzenia unikalnego pliku lub katalogu tymczasowego ( -d).

kenorb
źródło
2
nie dokładnie to, o co pytano. Jednak może to być idealne rozwiązanie.
jpbochi
1
@jpbochi Poprawiłem odpowiedź, aby odpowiedzieć na pytanie. Jeśli to pomoże, to daj mi znać.
kenorb
2
Rzeczywiście poprawia odpowiedź. Jednak mój głos był już twój. Nie możesz więcej głosować. Jedną z sugestii, które mam, jest wyjaśnienie, co ${0##*/}i $$rozwinięcie lub link do jakiejś dokumentacji na ten temat.
jpbochi
0

mktemp jest prawdopodobnie najbardziej wszechstronny, zwłaszcza jeśli planujesz trochę pracować z plikiem.

Możesz również użyć operatora podstawiania procesu, <() jeśli potrzebujesz pliku tylko tymczasowo jako dane wejściowe dla innego polecenia, np .:

$ diff <(echo hello world) <(echo foo bar)
Barry Jones
źródło