103

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

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

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

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

或者也许有更好的解决方案?

4

8 回答 8

133

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

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

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

于 2009-03-26T18:42:28.733 回答
109

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

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

或者,我最喜欢的 unix-isms 之一是打开一个文件,然后在你仍然打开它的时候删除它。该文件保留在文件系统上,您可以对其进行读写,但是一旦您的程序退出,该文件就会消失。不过,不确定如何在 bash 中做到这一点。

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

于 2009-03-26T18:32:01.427 回答
28

您想使用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
于 2009-03-26T18:34:49.343 回答
11

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

trap "{ rm -f $LOCKFILE }" EXIT

dash只能在 bash 中工作(如果 shell 是或 classic ,它不会捕获 Ctrl+c sh),但如果你想要兼容性,那么你仍然需要枚举你想要捕获的所有信号。

另请记住,当脚本退出时,始终执行信号“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时运行一些代码,如果需要,您可以在实际 EXIT 信号处运行一些代码(退出的最后阶段)

于 2017-01-09T19:57:05.433 回答
5

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

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

于 2009-03-28T23:35:00.730 回答
5

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

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

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

于 2020-04-24T13:45:56.360 回答
2

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

tmp=$(tempfile -s "your_sufix")
trap "rm -f '$tmp'" exit
于 2011-07-21T09:35:26.690 回答
-5

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

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

于 2009-03-26T18:33:45.900 回答