Bagaimana cara membuat file sementara di skrip shell?

155

Saat menjalankan skrip, saya ingin membuat file sementara di /tmpdirektori.

Setelah eksekusi skrip itu, itu akan dibersihkan oleh skrip itu.

Bagaimana cara melakukannya dalam skrip shell?

Bhuvanesh
sumber

Jawaban:

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

Anda dapat memastikan bahwa sebuah file dihapus ketika skrip keluar (termasuk kill dan crash) dengan membuka deskriptor file ke file dan menghapusnya. File tetap tersedia (untuk skrip; tidak benar-benar untuk proses lain tetapi /proc/$PID/fd/$FDdapat diselesaikan) selama deskriptor file terbuka. Ketika itu ditutup (yang kernel lakukan secara otomatis ketika proses keluar) filesystem menghapus file.

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3
Hauke ​​Laging
sumber
4
Jawaban yang bagus, solusi elegan dengan deskriptor file jika terjadi kerusakan +1
chaos
2
/proc- Kecuali untuk sistem yang tidak memilikinya.
Dennis Williamson
4
apa yang exec 3> "$tmpfile"dilakukan? Bukankah itu hanya berguna jika tmpfile adalah skrip yang berdiri sendiri?
Alexej Magura
5
Bagaimana Anda membaca dari FD yang dibuat?
eckes
3
"Kamu bisa menggunakan cat <3 atau yang serupa." sebenarnya itu berbunyi dari file bernama 3 @ dragon788. Juga, cat <&3akan memberi Bad file descriptor. Saya akan menghargai jika Anda memperbaikinya atau menghapusnya; informasi yang salah tidak banyak membantu.
Daniel Farrell
65

Gunakan mktempuntuk membuat file atau direktori sementara:

temp_file=$(mktemp)

Atau untuk direcotry:

temp_dir=$(mktemp -d)

Di akhir skrip Anda harus menghapus file / dir sementara:

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

mktemp membuat file di /tmpdirektori atau di dalam drectory yang diberikan dengan --tmpdirargumen.

kekacauan
sumber
20
Anda dapat menggunakan trap "rm -f $temp_file" 0 2 3 15tepat setelah membuat file sehingga ketika skrip keluar atau dihentikan dengan ctrl-Cfile tersebut masih dihapus.
Wurtel
1
@wurtel Apa yang terjadi jika EXIThanya kait untuk trap?
Hauke ​​Laging
4
@ HaukeLaging Maka jebakan tidak menyala jika script dihentikan dengan Ctrl + C. Satu hal yang perlu diperhatikan adalah bahwa TRAP tidak membantu jika Anda kill -9 $somepid. Sinyal pembunuhan khusus itu adalah kematian tanpa kejadian lain.
dragon788
5
@ dragon788 Sudahkah Anda mencobanya? Kamu harus bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging
Menjebak EXITsudah cukup.
Kusalananda
15

Jika Anda menggunakan sistem yang memiliki mktemp , Anda harus menggunakannya sebagai jawaban lain.

Dengan POSIX toolchest:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"
cuonglm
sumber
Apa yang terjadi jika EXIThanya kait untuk trap?
Hauke ​​Laging
@ HaukeLaging: tmpfilemasih bisa dihapus sebelum skrip keluar, tetapi tidak ketika skrip menerima sinyal lain.
cuonglm
Bukan itu yang terjadi di sini (GNU bash, Versi 4.2.53).
Hauke ​​Laging
@ HaukeLaging: Apa maksudmu That's not what happens?
cuonglm
3
mktempberasal dari HP / UX dengan sintaks yang berbeda. Todd C. Miller menciptakan yang berbeda untuk OpenBSD pada pertengahan 90-an (disalin oleh FreeBSD dan NetBSD) dan kemudian membuatnya juga tersedia sebagai utilitas mandiri (www.mktemp.org). Itulah yang biasanya digunakan di Linux sampai mktemputilitas (kebanyakan yang kompatibel) ditambahkan ke GNU coreutils pada tahun 2007. Hanya untuk mengatakan orang tidak bisa benar-benar mengatakan mktempadalah utilitas GNU.
Stéphane Chazelas
14

Beberapa shell memiliki fitur bawaan.

zsh

zsh's =(...)bentuk substitusi proses menggunakan file sementara. Misalnya =(echo test)memperluas ke jalur file sementara yang berisi 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

File itu secara otomatis dihapus, setelah perintah selesai.

bash / zsh di Linux.

File di sini atau di sini-string di bashdan zshdiimplementasikan sebagai file sementara yang dihapus.

Jadi, jika Anda melakukannya:

exec 3<<< test

File descriptor 3 terhubung ke file sementara dihapus yang berisi test\n.

Anda bisa mendapatkan kontennya dengan:

cat <&3

Jika di Linux, Anda juga dapat membaca atau menulis ke file itu melalui /dev/fd/3

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

(beberapa kerang lain menggunakan pipa, atau dapat digunakan /dev/nulljika dokumen di sini kosong).

POSIX

Tidak ada mktemputilitas POSIX. Namun POSIX menentukan mkstemp(template)API C , dan m4utilitas standar memperlihatkan API itu dengan mkstemp()fungsi m4 dengan nama yang sama.

mkstemp()memberi Anda nama file dengan bagian acak yang dijamin tidak ada pada saat fungsi dipanggil. Itu membuat file dengan izin 0600 dengan cara bebas ras.

Jadi, Anda bisa melakukan:

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

Namun perlu dicatat bahwa Anda perlu menangani pembersihan saat keluar, meskipun jika Anda hanya perlu menulis dan membaca file beberapa kali, Anda dapat membukanya dan menghapusnya setelah membuat seperti untuk di sini-doc / di sini- pendekatan string di atas:

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

Anda dapat membuka file untuk membaca sekali, dan memundurkan di antara dua bacaan, namun tidak ada utilitas POSIX yang dapat melakukan rewinding ( lseek()), sehingga Anda tidak dapat melakukannya dengan mudah dalam skrip POSIX ( zsh( sysseekbuiltin) dan ksh93( <#((...))operator) dapat lakukan saja).

Stéphane Chazelas
sumber
1
Bash juga memiliki proses substitusi menggunakan<()
WinnieNicklaus
3
@ WininnNicklaus, ya, tapi itu tidak menggunakan file sementara jadi tidak relevan di sini. Substitusi proses diperkenalkan oleh ksh, disalin oleh bash dan zsh, dan zsh diperpanjang dengan bentuk 3: =(...).
Stéphane Chazelas
7

Berikut ini adalah jawaban yang sedikit lebih baik di jajaran 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
Angsa
sumber
2
Perlu dicatat bahwa konten hanya tersedia sekali. Yaitu jika saya melakukan cat <& $ FD_R untuk kedua kalinya, tidak ada output yang dihasilkan. Lihat unix.stackexchange.com/questions/166482/… . Apakah ada cara untuk menghapus file secara otomatis jika program macet, tetapi membuatnya dapat diakses beberapa kali?
smihael
0

Alur kerja saya biasanya dengan file temp adalah karena beberapa skrip bash yang saya uji. Saya ingin teeitu jadi saya bisa melihat itu berfungsi dan menyimpan output untuk iterasi selanjutnya dari proses saya. Saya telah membuat file bernamatmp

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

sehingga saya bisa menggunakannya seperti

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

Alasan saya menyukai datetime yang diformat sebelum nilai-nilai acak adalah ia memungkinkan saya menemukan file tmp yang baru saja saya buat dengan mudah, dan saya tidak perlu memikirkan apa nama untuk itu waktu berikutnya (dan fokus hanya pada mendapatkan skrip dang saya bekerja).

Frank Bryce
sumber