在意外的 bash 退出中删除已创建的临时文件

我正在从 bash 脚本创建临时文件。我将在处理结束时删除它们,但是由于脚本运行了相当长的时间,如果在运行过程中杀死它或者仅仅使用 CTRL-C,临时文件就不会被删除。
有没有办法在执行结束前捕捉到这些事件并清理文件?

另外,对于这些临时文件的命名和位置是否有某种最佳实践?
我目前还不确定是否使用:

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

还有

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

Or maybe is there some better solutions?

78395 次浏览

您可以设置一个“ 陷阱”在 exit 上执行,或者设置一个 control-c 来清理。

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

或者,我最喜欢的一个统一主义是打开一个文件,然后删除它,而你仍然打开它。文件保存在文件系统中,您可以读写它,但是一旦程序退出,文件就会消失。不过不知道你在 bash 里会怎么做。

顺便说一下: 我支持 mktemp 而不是使用您自己的解决方案: 如果用户预期您的程序将创建巨大的临时文件,他可能希望将 TMPDIR设置为更大的位置,如/var/tmp。Mktemp 认识到这一点,但是手工卷制的解决方案(第二个选项)不能。例如,我经常使用 TMPDIR=/var/tmp gvim -d foo bar

您不必费心删除那些用 mktemp 创建的 tmp 文件。

如果可以,使用 mktemp,因为它会生成比“ $$”前缀更独特的文件。而且看起来更像是跨平台的方式来创建临时文件,然后显式地将它们放入/tmp。

您希望使用 陷阱命令来处理退出脚本或 CTRL-C 之类的信号。有关详细信息,请参阅 格雷格的维基百科

对于您的临时文件,使用 basename $0是一个好主意,同时提供一个模板,为足够的临时文件提供空间:

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


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


trap 'rm -f $TMP1 $TMP2' EXIT

我通常创建一个目录来放置所有的临时文件,然后立即创建一个 EXIT 处理程序,在脚本退出时清理这个目录。

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

如果您将所有的临时文件放在 $MYTMPDIR下,那么在大多数情况下,当您的脚本退出时,它们都将被删除。用 SIGKILL (kill -9)杀死一个进程会立即杀死该进程,因此您的 EXIT 处理程序在这种情况下不会运行。

使用带有 $$的可预测文件名的替代方法是一个巨大的安全漏洞,您永远不应该、永远不应该考虑使用它。即使它只是一个简单的个人脚本上您的单一用户电脑。这是一个你不应该养成的坏习惯。BugTraq充满了“不安全的临时文件”事件。有关临时文件的安全方面的更多信息,请参见 给你给你给你

我最初想引用不安全的 TMP1和 TMP2作业,但转念一想,这可能是 这不是个好主意

我更喜欢使用 tempfile,它以安全的方式在/tmp 中创建一个文件,您不必担心它的命名:

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

Just keep in mind that choosen answer is bashism, which means solution as

trap "{ rm -f $LOCKFILE }" EXIT

只能在 bash 中工作(如果 shell 是 dash或经典的 sh,则不能捕获 Ctrl + c) ,但是如果希望兼容,那么仍然需要枚举所有希望捕获的信号。

Also keep in mind that when script exits the trap for signal "0"(aka EXIT) is always performed resulting in double execution of trap command.

如果有出口信号,不要将所有信号堆叠在一条线上。

为了更好地理解它,请看下面的脚本,它可以在不改变的情况下跨不同的系统工作:

#!/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函数) ,如果需要的话,你可以运行一些代码的实际退出信号(退出的最后阶段)

我不能相信这么多人认为一个文件名不会包含一个空格。如果 $TMPDIR 被分配到“临时目录”,世界将崩溃。

zTemp=$(mktemp --tmpdir "$(basename "$0")-XXX.ps")
trap "rm -f ${zTemp@Q}" EXIT

空格和其他特殊字符,如文件名中的单引号和回车符,应该在代码中考虑,作为良好编程习惯的要求。