Як створити тимчасовий файл у скрипті оболонки?


155

Під час запуску сценарію я хочу створити тимчасовий файл у /tmpкаталозі.

Після виконання цього сценарію цей сценарій буде очищений.

Як це зробити в скрипті оболонки?

Відповіді:


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

Ви можете переконатися, що при видаленні скриптів файл видаляється (включаючи вбивства та збої), відкривши дескриптор файлу та видаливши його. Файл залишається доступним (для сценарію; насправді не для інших процесів, але /proc/$PID/fd/$FDє обхідним), поки дескриптор файлу відкритий. Коли воно закривається (що ядро ​​робить автоматично, коли процес закінчується), файлова система видаляє файл.

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

4
Хороша відповідь, елегантне рішення з дескриптором файлів у разі аварії +1
хаос

2
/proc- за винятком систем, у яких його немає.
Денніс Вільямсон

4
що робить exec 3> "$tmpfile"? Хіба це не тільки корисно, якщо tmpfile є автономним сценарієм?
Олексій Магура

5
Як ви читаєте зі створеного FD?
eckes

3
"Ви можете використовувати cat <3 або щось подібне." насправді, що читається з файлу на ім'я 3 @ dragon788. Також, cat <&3дадуть Bad file descriptor. Буду вдячний, якщо ви його виправите чи вилучите; дезінформація не дуже допомагає.
Даніель Фаррелл

65

Використовуйте mktempдля створення тимчасового файлу чи каталогу:

temp_file=$(mktemp)

Або для режисери:

temp_dir=$(mktemp -d)

Наприкінці сценарію вам потрібно видалити тимчасовий файл / dir:

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

mktemp створює файл у /tmpкаталозі або в деривації, заданому --tmpdirаргументом.


20
Ви можете використовувати trap "rm -f $temp_file" 0 2 3 15відразу після створення файлу, щоб, коли скрипт вийшов або зупинився, ctrl-Cфайл все-таки видалився.
wurtel

1
@wurtel Що станеться, якщо EXITце єдиний гачок trap?
Hauke ​​Laging

4
@HaukeLaging Тоді пастка не спрацьовує, якщо сценарій зупинено Ctrl + C. Варто зазначити, що TRAP не допомагає вам kill -9 $somepid. Цей конкретний сигнал вбивства - це смерть, і нічого іншого не відбувається.
dragon788

5
@ dragon788 Ви пробували це? Ти повинен. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging

Пастки EXITдосить.
Kusalananda

15

Якщо ви працюєте в системі, яка має mktemp , вам слід використовувати її як інші відповіді.

З інструментальним інструментом POSIX:

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

Що станеться, якщо EXITце єдиний гачок trap?
Hauke ​​Laging

@HaukeLaging: tmpfileвсе-таки видаляйте перед виходом сценарію, але не тоді, коли сценарій отримав інші сигнали.
cuonglm

Це не те, що відбувається тут (GNU bash, версія 4.2.53).
Hauke ​​Laging

@HaukeLaging: Що ти маєш на увазі That's not what happens?
cuonglm

3
mktempвиник у HP / UX з іншим синтаксисом. У середині 90-х Тод К. Міллер створив інший для OpenBSD (скопійований FreeBSD та NetBSD), а згодом також став доступним як окрема утиліта (www.mktemp.org). Це той, який зазвичай використовувався в Linux, поки mktempв 2007 році до ядра GNU не була додана (в основному сумісна) утиліта. Просто сказати, що реально не можна сказати mktemp, це утиліта GNU.
Стефан Шазелас

14

Деякі снаряди мають вбудовану функцію.

зш

zsh«S =(...)форма заміщення процесу використовує тимчасовий файл. Наприклад, =(echo test)розширюється до шляху тимчасового файлу, який містить 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

Цей файл автоматично видаляється після завершення команди.

bash / zsh в Linux.

Тут-файли або тут-рядки в bashі zshреалізуються як видалені тимчасові файли.

Отже, якщо ви робите:

exec 3<<< test

Дескриптор файлу 3 підключений до видаленого тимчасового файлу, який містить test\n.

Ви можете отримати його вміст за допомогою:

cat <&3

Якщо в Linux, ви також можете читати або записувати в цей файл через /dev/fd/3

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

(деякі інші оболонки використовують труби, або можуть використовувати їх, /dev/nullякщо тут документ пустий).

POSIX

Немає mktempутиліти POSIX. POSIX, однак, визначає mkstemp(template)API C , і m4стандартна утиліта розкриває цей API з mkstemp()функцією m4 з тим же ім'ям.

mkstemp()дає вам ім'я файлу з випадковою частиною, яка гарантувалась, що не існує на момент виклику функції. Він створює файл з дозволами 0600 без гонки.

Отже, ви можете зробити:

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

Однак зауважте, що вам потрібно обробити очищення після виходу, хоча якщо вам потрібно лише записати та прочитати файл фіксовану кількість разів, ви можете відкрити його та видалити відразу після створення, як для here-doc / here- рядковий підхід вище:

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

Ви можете відкрити файл для читання один раз і перемотати назад між двома читаннями, однак не існує утиліти POSIX, яка могла б зробити це перемотування ( lseek()), тому ви не можете робити це портативно в сценарії POSIX ( zsh( sysseekвбудований) та ksh93( <#((...))оператор) зробіть це, хоча).


1
Bash також має процедуру заміни, використовуючи<()
WinnieNicklaus

3
@WinnieNicklaus, так, але це не використовує тимчасові файли, тому тут не має значення. Підстановка процесів був введений KSH, скопійований Баш і Zsh, і ЗШ продовжив його з 3 - ю формою: =(...).
Стефан Шазелас

7

Ось трохи покращена відповідь у рядку Хауке Лагінга:

#!/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
Слід зазначити, що вміст доступний лише один раз. Тобто, якщо я вдруге заробляю кошти <& $ FD_R, вихід не виробляється. Дивіться unix.stackexchange.com/questions/166482/… . Чи є спосіб автоматично видалити файл, якщо програма виходить з ладу, але зробивши його доступним кілька разів?
smihael

0

Мій робочий процес, як правило, з тимчасовими файлами, пов'язаний з певним сценарієм bash, який я тестую. Я хочу teeце зробити, щоб я міг бачити, що він працює, і зберегти висновок для наступної ітерації мого процесу. Я створив файл під назвоюtmp

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

щоб я міг нею користуватися

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

Причина, що мені подобається відформатовану дату перед випадковими значеннями, - це дозволяє мені знайти файл tmp, який я щойно створив, і мені не потрібно думати про те, як назвати його наступного разу (і зосередитися на тому, щоб просто отримати мій сценарій dang працювати).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.