Jak utworzyć plik tymczasowy w skrypcie powłoki?


155

Podczas uruchamiania skryptu chcę utworzyć plik tymczasowy w /tmpkatalogu.

Po wykonaniu tego skryptu zostanie on wyczyszczony przez ten skrypt.

Jak to zrobić w skrypcie powłoki?

Odpowiedzi:


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

Możesz upewnić się, że plik zostanie usunięty po wyjściu ze skryptu (włączając zabójstwa i awarie), otwierając deskryptor pliku i usuwając go. Plik pozostaje dostępny (dla skryptu; tak naprawdę nie dla innych procesów, ale /proc/$PID/fd/$FDjest obejściem), dopóki deskryptor pliku jest otwarty. Po zamknięciu (co jądro robi automatycznie po zakończeniu procesu) system plików usuwa plik.

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

4
Dobra odpowiedź, eleganckie rozwiązanie z deskryptorem pliku na wypadek awarii +1
— chaos

2
/proc- z wyjątkiem systemów, które go nie mają.
— Dennis Williamson

4
co robi exec 3> "$tmpfile"zrobić? Czy to nie jest użyteczne tylko wtedy, gdy plik tmp jest samodzielnym skryptem?
— Alexej Magura

5
Jak czytasz z utworzonego FD?
— eckes,

3
„Możesz użyć kota <3 lub czegoś podobnego”. właściwie to czyta z pliku o nazwie 3 @ dragon788. Również cat <&3da Bad file descriptor. Byłbym wdzięczny, jeśli to naprawisz lub usuniesz; dezinformacja niewiele pomaga.
— Daniel Farrell

65

Użyj, mktempaby utworzyć tymczasowy plik lub katalog:

temp_file=$(mktemp)

Lub w kierunku direcotry:

temp_dir=$(mktemp -d)

Na końcu skryptu musisz usunąć plik tymczasowy / katalog:

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

mktemp tworzy plik w /tmpkatalogu lub w drectory podanym z --tmpdirargumentem.


20
Możesz użyć trap "rm -f $temp_file" 0 2 3 15zaraz po utworzeniu pliku, aby po wyjściu lub zatrzymaniu skryptu ctrl-Cplik był nadal usuwany.
— wurtel

1
@wurtel Co się stanie, jeśli EXITjest to jedyny hak trap?
— Hauke ​​Laging

4
@HaukeLaging Wtedy pułapka nie zostanie uruchomiona, jeśli skrypt zostanie zatrzymany za pomocą Ctrl + C. Należy zauważyć, że TRAP nie pomaga, jeśli ty kill -9 $somepid. Ten szczególny sygnał zabójstwa to natychmiastowa śmierć i nic innego się nie dzieje.
— dragon788

5
@ dragon788 Czy próbowałeś tego? Powinieneś. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
— Hauke ​​Laging

EXITWystarczą pułapki .
— Kusalananda

15

Jeśli korzystasz z systemu, który ma mktemp , powinieneś użyć go jako innych odpowiedzi.

Z zestawem narzędzi POSIX:

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

Co się stanie, jeśli EXITjest to jedyny hak trap?
— Hauke ​​Laging

@ HaukeLaging: tmpfilenadal będzie usuwany przed zakończeniem skryptu, ale nie, gdy skrypt otrzyma inne sygnały.
— cuonglm

To nie dzieje się tutaj (GNU bash, wersja 4.2.53).
— Hauke ​​Laging

@HaukeLaging: Co masz na myśli That's not what happens?
— cuonglm

3
mktemppochodzi z HP / UX z inną składnią. Todd C. Miller stworzył inny dla OpenBSD w połowie lat 90. (skopiowany przez FreeBSD i NetBSD), a później udostępnił go również jako samodzielne narzędzie (www.mktemp.org). Jest to ten, który był zwykle używany w Linuksie, dopóki mktempw 2007 r. Nie dodano (głównie kompatybilnego) narzędzia do GNU coreutils. Nie można powiedzieć, że tak naprawdę nie można powiedzieć, że mktempto narzędzie GNU.
— Stéphane Chazelas

14

Niektóre muszle mają wbudowaną funkcję.

zsh

zshjest =(...)podstawienia proces wykorzystuje plik tymczasowy. Na przykład =(echo test)rozwija się do ścieżki pliku tymczasowego, który zawiera 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

Plik ten jest automatycznie usuwany po zakończeniu wykonywania polecenia.

bash / zsh w systemie Linux.

Pliki-tutaj lub ciągi-tu są bashi zshsą implementowane jako usunięte pliki tymczasowe.

Więc jeśli to zrobisz:

exec 3<<< test

Deskryptor pliku 3 jest połączony z usuniętym plikiem tymczasowym, który zawiera test\n.

Możesz uzyskać jego zawartość za pomocą:

cat <&3

W systemie Linux można również czytać lub zapisywać do tego pliku za pośrednictwem /dev/fd/3

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

(niektóre inne powłoki używają potoków lub mogą użyć, /dev/nulljeśli tutaj dokument jest pusty).

POSIX

Nie ma mktempnarzędzia POSIX. POSIX określa jednak mkstemp(template)interfejs API języka C , a m4standardowe narzędzie udostępnia interfejs API z mkstemp()funkcją m4 o tej samej nazwie.

mkstemp()daje nazwę pliku z losową częścią, która nie istniała w momencie wywołania funkcji. Tworzy plik z uprawnieniami 0600 w sposób bez wyścigu.

Więc możesz zrobić:

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

Zauważ jednak, że musisz wyjść z czyszczenia po wyjściu, ale jeśli musisz tylko napisać i odczytać plik określoną liczbę razy, możesz go otworzyć i usunąć zaraz po utworzeniu, jak w przypadku tutaj-doc / here- powyższe podejście łańcuchowe:

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

Możesz otworzyć plik do odczytu raz i przewinąć do tyłu między dwoma odczytami, jednak nie ma narzędzia POSIX, które może zrobić to przewijanie ( lseek()), więc nie możesz tego zrobić przenośnie w skrypcie POSIX ( zsh( sysseekwbudowany) i ksh93( <#((...))operator) może zrób to jednak).


1
Bash ma również podstawianie procesów przy użyciu<()
— WinnieNicklaus

3
@WinnieNicklaus, tak, ale to nie korzysta z plików tymczasowych, więc tutaj nie ma znaczenia. Zmiana procesu został wprowadzony KSH kopiowane bash i zsh i zsh rozszerzył się 3 postać: =(...).
— Stéphane Chazelas,

7

Oto nieco ulepszona odpowiedź w linii 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
Należy zauważyć, że treść jest dostępna tylko raz. To znaczy, jeśli zrobię cat <& $ FD_R po raz drugi, nie zostanie wygenerowany żaden wynik. Zobacz unix.stackexchange.com/questions/166482/… . Czy istnieje jakiś sposób, aby plik został automatycznie usunięty, jeśli program ulegnie awarii, ale zostanie udostępniony wiele razy?
— smihael

0

Mój przepływ pracy zwykle z plikami tymczasowymi wynika z testowanego skryptu bash. Chcę teeto poprawić, aby zobaczyć, że działa, i zapisać dane wyjściowe do następnej iteracji mojego procesu. Utworzyłem plik o nazwietmp

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

żebym mógł go używać jak

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

Powodem, dla którego podoba mi się data i godzina sformatowana przed losowymi wartościami, jest to, że pozwala mi łatwo znaleźć plik tmp, który właśnie utworzyłem, i nie muszę myśleć o tym, jak nazwać go następnym razem (i skupić się na otrzymaniu skryptu Dang pracować).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.