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


89

我正在从bash脚本创建临时文件。我将在处理结束时删除它们,但是由于脚本运行了很长时间,因此,如果我在运行过程中将其杀死或仅按CTRL-C杀死,则不会删除临时文件。
有没有办法可以在执行结束之前捕获这些事件并清理文件?

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

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

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

还是有更好的解决方案?


Answers:


97

您可以设置一个“ 陷阱 ”以在退出时执行或在control-c上执行以进行清理。

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

另外,我最喜欢的unix-ism之一是打开一个文件,然后在您仍将其打开的同时将其删除。该文件保留在文件系统上,您可以对其进行读写,但是一旦程序退出,该文件就会消失。不过,不确定如何在bash中执行此操作。

顺便说一句,我会推荐使用mktemp而不是使用您自己的解决方案:如果用户期望您的程序将要创建巨大的临时文件,则他可能希望将其设置TMPDIR为更大的位置,例如/ var / tmp。mktemp意识到,您的手动解决方案(第二个选项)没有。TMPDIR=/var/tmp gvim -d foo bar例如,我经常使用。


8
使用bash,exec 5<>$TMPFILE领带文件描述符5至$ TMPFILE为读写,并且可以使用<&5>&5以及/proc/$$/fd/5其后(Linux)的。唯一的问题是Bash缺乏seek功能……
短暂

接受您的回答,因为您提供的链接可以最好地解释我的需求。谢谢
skinp

4
关于以下几点的注意事项trap:没有陷阱SIGKILL(按设计,因为它会立即终止执行)。因此,如果发生这种情况,请制定一个后备计划(例如tmpreaper)。其次,陷阱不是累积的-如果要执行的动作不只一个,它们必须全部在trap命令中。解决多种清除操作的一种方法是定义一个函数(如果需要,您可以在程序执行时重新定义它)并引用该函数:trap cleanup_function EXIT
Toby Speight

1
我不得不使用,trap "rm -f $LOCKFILE" EXIT否则我将收到意外的文件错误结束。
Jaakko

3
Shellcheck发出警告,请使用单引号,该表达式将使用双引号“现在”展开,而不是稍后在调用陷阱时将其展开。
LaFayette

110

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

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

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


27
+1一定要在EXIT上使用陷阱,而不要愚蠢的TERM / INT / HUP /其他您能想到的。虽然,请记住引用参数扩展,并且我建议您引号陷阱:
trap'rm

7
用单引号引起来,因为如果以后由于环境原因而决定清理并更改TMPDIR,则陷阱仍将起作用。
lhunath

1
@AaronDigulla为什么$()vs反引号很重要?
Ogre Psalm33


3
@AlexanderTorstling代码应始终单引号,以防止注入导致任意代码执行。如果将数据扩展为bash代码STRING,则该数据现在可以执行任何代码操作,这将导致无害的空格空白错误,而且还会产生破坏性的错误,例如出于奇怪的原因清除homedir或引入安全漏洞。请注意,trap接受一串bash代码,稍后将对其进行评估。因此,稍后在陷阱触发时,单引号将消失,而语法上只有双引号。
lhunath

25

您想使用trap命令处理退出脚本或CTRL-C之类的信号。有关详细信息,请参见Greg的Wiki

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

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

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

trap 'rm -f $TMP1 $TMP2' EXIT

1
不要陷在TERM / INT上。退出时陷阱。试图根据接收到的信号来预测出口情况是愚蠢的,而且绝对不是一个万能的选择。
lhunath

3
次要点:使用$()代替单个反引号。并在$ 0前后加上双引号,因为它可能包含空格。
亚伦·迪古拉

很好,反引号在此注释中工作正常,但这是一个公平的观点,养成使用的习惯是很好的$()。还添加了双引号。
布赖恩·坎贝尔

1
您可以只用TMP1 = $(tempfile -s“ XXXXXX”)替换整个子例程
Ruslan Kabalin 2011年

4
@RuslanKabalin并非所有系统都有tempfile命令,而我所知道的所有合理的现代系统都有mktemp命令。
布莱恩·坎贝尔

9

请记住,选择的答案是bashism,这意味着解决方案是

trap "{ rm -f $LOCKFILE }" EXIT

只能在bash中工作(如果shell是dash或者classic sh,它将无法捕获Ctrl + c ),但是如果您希望兼容,则仍然需要枚举所有要捕获的信号。

还请记住,当脚本退出时,始终会执行信号“ 0”(又名EXIT)的陷阱,从而导致trap命令的两次执行。

就是说如果有EXIT信号,则不将所有信号堆叠在一行中的原因。

为了更好地理解它,请查看以下脚本,该脚本无需更改即可在不同系统上运行:

#!/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功能)之前,在实际信号出现时运行一些代码,如果需要,您可以在实际退出信号(退出的最后阶段)运行一些代码。


4

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

我起初是想引用不安全的TMP1和TMP2分配,但后来我又想那可能不是一个好主意


如果可以,我会给:+1表示安全建议,而+1表示不引用不好的主意和参考
TMG

1

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

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

遗憾的是,临时文件虽然更安全,但却非常不可移植,因此最好避免使用或至少模仿它。
lericson

1

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

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

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


+1虽然单引号trap 'rm -f "${zTemp}"' EXIT正确处理了空格和其他特殊字符,但此答案的解决方案不会延迟对的评估zTemp。因此,无需担心zTemp稍后在脚本中进行更改的价值。同样,zTemp可以声明为函数本地的;它不必是全局脚本变量。
罗宾·米德

分配的RHS周围的双引号是不必要的。
罗宾·米德

应该注意的是,这些${parameter@operator}扩展是在Bash 4.4(2016年9月发布)中添加的。
罗宾·米德

-4

您不必费心删除使用mktemp创建的tmp文件。无论如何,它们将在以后删除。

如果可以,请使用mktemp,因为它会生成更多的唯一文件,而不是前缀“ $$”。而且看起来更跨平台的方式可以创建临时文件,然后将其显式放入/ tmp。


4
由谁或什么删除?
innaM 2009年

一段时间后由操作系统|文件系统本身删除了
Mykola Golubyev 2009年

4
魔法?cronjob?还是重新启动的Solaris计算机?
innaM 2009年

可能是其中之一。如果临时文件没有因临时中断而被删除(这种情况不会经常发生),则有一天tmp文件将被删除-这就是为什么它们称为temp的原因。
Mykola Golubyev 09年

21
您不能也不应该不假设放在/ tmp中的内容将永远存在;同时,您不应该假设它会神奇地消失。
innaM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.