Удаление созданных временных файлов при неожиданном завершении bash
-
22-08-2019 - |
Вопрос
Я создаю временные файлы из скрипта bash.Я удаляю их в конце обработки, но поскольку скрипт выполняется довольно долго, если я отключу его или просто нажму CTRL-C во время запуска, временные файлы не будут удалены.
Есть ли способ, которым я могу перехватить эти события и очистить файлы до завершения выполнения?
Кроме того, существует ли какая-то наилучшая практика для именования и расположения этих временных файлов?
В настоящее время я не уверен, использовать ли:
TMP1=`mktemp -p /tmp`
TMP2=`mktemp -p /tmp`
...
и
TMP1=/tmp/`basename $0`1.$$
TMP2=/tmp/`basename $0`2.$$
...
Или, может быть, есть какие-то лучшие решения?
Решение
Вы могли бы установить "ловушка" выполнить при выходе или на элементе управления-c для очистки.
trap "{ rm -f $LOCKFILE; }" EXIT
В качестве альтернативы, один из моих любимых unix-измов - открыть файл, а затем удалить его, пока он еще открыт.Файл остается в файловой системе, и вы можете читать и записывать его, но как только ваша программа завершает работу, файл исчезает.Хотя не уверен, как бы вы сделали это в bash.
КСТАТИ:Один аргумент, который я приведу в пользу mktemp вместо того, чтобы использовать ваше собственное решение:если пользователь ожидает, что ваша программа собирается создавать огромные временные файлы, он может захотеть установить TMPDIR
куда-нибудь побольше, например, в /var/tmp.mktemp распознает это, ваше решение ручной сборки (второй вариант) - нет.Я часто использую TMPDIR=/var/tmp gvim -d foo bar
, например.
Другие советы
Обычно я создаю каталог, в который помещаю все свои временные файлы, а затем сразу после этого создаю обработчик ВЫХОДА для очистки этого каталога при завершении работы скрипта.
MYTMPDIR=$(mktemp -d)
trap "rm -rf $MYTMPDIR" EXIT
Если вы поместите все свои временные файлы в $MYTMPDIR
, то все они будут удалены при завершении работы вашего скрипта в большинстве случаев.Однако уничтожение процесса с помощью SIGKILL (kill -9) сразу же убивает процесс, поэтому ваш обработчик ВЫХОДА в этом случае запускаться не будет.
Вы хотите использовать ловушка команда для обработки выхода из скрипта или сигналов, подобных CTRL-C.Смотрите на Вики Грега для получения подробной информации.
Для ваших временных файлов, используя basename $0
это хорошая идея, а также предоставление шаблона, который предоставляет место для достаточного количества временных файлов:
tempfile() {
tempprefix=$(basename "$0")
mktemp /tmp/${tempprefix}.XXXXXX
}
TMP1=$(tempfile)
TMP2=$(tempfile)
trap 'rm -f $TMP1 $TMP2' EXIT
Просто имейте в виду, что выбранный ответ - это bashism
, что означает решение в виде
trap "{ rm -f $LOCKFILE }" EXIT
будет работать только в bash (он не будет ловить Ctrl + c, если оболочка dash
или классический sh
), но если вы хотите получить совместимость, то вам все равно нужно перечислить все сигналы, которые вы хотите перехватить.
Также имейте в виду, что при выходе скрипта всегда выполняется перехват сигнала "0" (он же EXIT), что приводит к двойному выполнению trap
команда.
Это причина не складывать все сигналы в одну строку, если есть сигнал ВЫХОДА.
Чтобы лучше понять это, посмотрите на следующий скрипт, который будет работать в разных системах без изменений:
#!/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
функция) и если это необходимо, вы можете запустить некоторый код при фактическом сигнале ВЫХОДА (заключительная стадия выхода)
Альтернатива использованию предсказуемого имени файла с $$ - это зияющая дыра в безопасности, и вы никогда, никогда, никогда не должны думать о ее использовании.Даже если это всего лишь простой персональный скрипт на вашем однопользовательском компьютере.Это очень плохая привычка, которую вы не должны приобретать. Поиск ошибок полно инцидентов с "небезопасным временным файлом".Видишь здесь, здесь и здесь для получения дополнительной информации об аспекте безопасности временных файлов.
Изначально я думал процитировать небезопасные назначения TMP1 и TMP2, но, поразмыслив, это, вероятно, не очень хорошая идея.
Я предпочитаю использовать tempfile
который создает файл в / tmp безопасным способом, и вам не нужно беспокоиться о его именовании:
tmp=$(tempfile -s "your_sufix")
trap "rm -f '$tmp'" exit
Вам не нужно утруждать себя удалением этих tmp-файлов, созданных с помощью mktemp.Позже они все равно будут удалены.
Используйте mktemp, если можете, поскольку он генерирует больше уникальных файлов, чем префикс '$$'.И это выглядит как более кроссплатформенный способ создания временных файлов, а затем явного помещения их в / tmp.