Как создать временный файл в сценарии оболочки?

155

Во время выполнения скрипта я хочу создать временный файл в /tmpкаталоге.

После выполнения этого скрипта он будет очищен этим скриптом.

Как это сделать в сценарии оболочки?

Bhuvanesh
источник

Ответы:

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
«Вы можете использовать кошку <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?
Хауке Лагинг
4
@HaukeLaging Тогда ловушка не срабатывает, если скрипт останавливается с помощью Ctrl + C. Следует отметить, что TRAP не поможет, если вы kill -9 $somepid. Этот конкретный сигнал убийства - insta-death, и больше ничего не происходит.
dragon788
5
@ dragon788 Вы пробовали это? Вам следует. bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke ​​Laging
Ловушки EXITдостаточно.
Кусалананда
15

Если вы используете систему, в которой есть mktemp , вы должны использовать ее в качестве других ответов.

С помощью 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
источник
Что произойдет, если EXITэто единственный крюк для trap?
Хауке Лагинг
@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 coreutils не была добавлена (в основном совместимая) утилита. Просто сказать, что нельзя сказать, 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

Вот несколько улучшенный ответ в строке 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
Следует отметить, что контент доступен только один раз. Т.е., если я делаю cat <& $ FD_R во второй раз, вывод не производится. См. Unix.stackexchange.com/questions/166482/… . Есть ли способ автоматически удалить файл в случае сбоя программы, но сделать его доступным несколько раз?
Смайель
0

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

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

так что я могу использовать его как

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

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

Фрэнк Брайс
источник