Удаление созданных временных файлов в неожиданном bash выходе

Я создаю временные файлы из bash script. Я удаляю их в конце обработки, но поскольку script работает довольно долго, если я убью его или просто CTRL-C во время прогона, временные файлы не удаляются.
Есть ли способ поймать эти события и очистить файлы до завершения выполнения?

Кроме того, существует ли какая-то передовая практика для именования и размещения этих временных файлов?
В настоящее время я не уверен между использованием:

TMP1=`mktemp -p /tmp`
TMP2=`mktemp -p /tmp`
...

и

TMP1=/tmp/`basename $0`1.$$
TMP2=/tmp/`basename $0`2.$$
...

Или, может быть, есть несколько лучших решений?

Ответ 1

Вы можете установить " trap" для выполнения при выходе или с помощью элемента управления-c для очистки.

trap "{ rm -f $LOCKFILE; }" EXIT

В качестве альтернативы, один из моих любимых unix-isms - это открыть файл, а затем удалить его, пока вы все еще открываете его. Файл остается в файловой системе, и вы можете читать и записывать его, но как только ваша программа выйдет, файл исчезнет. Не уверен, как бы вы это делали в bash.

BTW: Один аргумент я дам в пользу mktemp вместо вашего собственного решения: если пользователь ожидает, что ваша программа собирается создавать огромные временные файлы, он может захотеть установить TMPDIR где-то больше, например /var/TMP. mktemp признает, что ваше ручное решение (второй вариант) этого не делает. Я часто использую TMPDIR=/var/tmp gvim -d foo bar, например.

Ответ 2

Обычно я создаю каталог для размещения всех моих временных файлов, а затем сразу же после этого создаю обработчик EXIT для очистки этого каталога, когда выйдет script.

MYTMPDIR=$(mktemp -d)
trap "rm -rf $MYTMPDIR" EXIT

Если вы поместите все свои временные файлы в $MYTMPDIR, то все они будут удалены, когда ваш script выйдет в большинстве случаев. Убийство процесса с помощью SIGKILL (kill -9) сразу же уничтожает процесс, поэтому ваш обработчик EXIT не будет работать в этом случае.

Ответ 3

Вы хотите использовать команду trap для обработки выхода из script или сигналов типа CTRL-C. Подробнее см. Advanced Bash Scripting Guide.

Для ваших tempfiles рекомендуется использовать basename $0, а также предоставить шаблон, который предоставляет место для достаточно временных файлов:

tempfile() {
    tempprefix=$(basename "$0")
    mktemp /tmp/${tempprefix}.XXXXXX
}

TMP1=$(tempfile)
TMP2=$(tempfile)

trap 'rm -f $TMP1 $TMP2' EXIT

Ответ 4

Альтернативой использования прогнозируемого имени файла с $$ является зияющая дыра в безопасности, и вы никогда не должны думать о ее использовании. Даже если это простой персональный script на вашем ПК с одним пользователем. Это очень плохая привычка, которую вы не должны получить. BugTraq заполнен инцидентами "небезопасного временного файла". См. здесь, здесь и здесь для получения дополнительной информации об аспекте безопасности временных файлов.

Я изначально думал о цитировании небезопасных назначений TMP1 и TMP2, но, во-вторых, подумал, что, вероятно, не будет хорошей идеей.

Ответ 5

Просто имейте в виду, что выбранный ответ bashism, что означает решение как

trap "{ rm -f $LOCKFILE }" EXIT

будет работать только в bash (он не поймает Ctrl + c, если оболочка dash или классическая sh), но если вам нужна совместимость, вам все равно нужно перечислить все сигналы, которые вы хотите захватить.

Также имейте в виду, что когда script выходит из ловушки для сигнала "0" (иначе EXIT), всегда выполняется, что приводит к двойному выполнению команды trap.

Чтобы не выставлять все сигналы в одной строке, если есть сигнал EXIT.

Чтобы лучше понять это, смотрите следующий script, который будет работать в разных системах без изменений:

#!/bin/sh

on_exit() {
  echo 'Cleaning up...(remove tmp files, etc)'
}

on_preExit() {
  echo
  echo 'Exiting...' # Runs just before actual exit,
                    # shell will execute EXIT(0) after finishing this function
                    # that we hook also in on_exit function
  exit 2
}


trap on_exit EXIT                           # EXIT = 0
trap on_preExit HUP INT QUIT TERM STOP PWR  # 1 2 3 15 30


sleep 3 # some actual code...

exit 

Это решение даст вам больше контроля, так как вы можете запустить часть своего кода при появлении фактического сигнала непосредственно перед окончательным выходом (функция preExit), и если это необходимо, вы можете запустить некоторый код при фактическом сигнале EXIT (заключительный этап выход)

Ответ 6

Я предпочитаю использовать tempfile, который создает файл в /tmp безопасным образом, и вам не нужно беспокоиться о его названии:

tmp=$(tempfile -s "your_sufix")
trap "rm -f '$tmp'" exit

Ответ 7

Вам не нужно беспокоиться об удалении этих файлов tmp, созданных с помощью mktemp. Они будут удалены в любом случае позже.

Используйте mktemp, если можете, поскольку он генерирует более уникальные файлы, а затем префикс $$. И это похоже на более кросс-платформенный способ создания временных файлов, а затем явно помещать их в/tmp.