English 中文(简体)
在意外的Bash退出中删除创建的临时文件
原标题:
  • 时间:2009-03-26 18:28:33
  •  标签:

I am creating temporary files from a bash script. I am deleting them at the end of the processing, but since the script is running for quite a long time, if I kill it or simply CTRL-C during the run, the temp files are not deleted.
Is there a way I can catch those events and: 和 clean-up the files before the execution ends?

Also, is there some kind of best practice for the naming and: 和 location of those temp files?
I m currently not sure between using:

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

and: 和

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

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

最佳回答

你可以设置一个“trap”在退出时执行或通过控制-c来清理。

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

或者,我最喜欢的UNIX-ISM之一是打开一个文件,然后在它仍然打开时将其删除。该文件保留在文件系统中,您可以读取和编写它,但是一旦您的程序退出,该文件会消失。不太确定您如何在bash中实现这一点。

BTW:我支持使用 mktemp 而不是使用自己的解决方案的一个论点是:如果用户预期您的程序将创建巨大的临时文件,他可能希望将 TMPDIR 设置为更大的某处,例如 /var/tmp。 mktemp 会识别,您手工编写的解决方案(第二个选项)不会。 比如,我经常使用 TMPDIR=/var/tmp gvim -d foo bar

问题回答

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

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

如果您将所有的临时文件存储在$MYTMPDIR目录下,那么在大多数情况下,当您的脚本退出时,它们都将被删除。然而,使用SIGKILL(kill -9)信号直接杀死进程会导致您的EXIT处理程序无法运行。

你想使用trap命令处理脚本退出或类似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中运行(如果 shell 是dash或经典的sh,它将捕获不了Ctrl+c),但如果你想要兼容性,那么你仍然需要枚举所有你想要捕获的信号。

请记住,当脚本退出时,信号“0”(也称为EXIT)的陷阱始终执行,导致trap命令执行两次。

如果有退出信号,则不要把所有信号都堆叠在一条线上,这是为了避免这种情况的发生。 (Zhè shì wèi le bìmiǎn zhè zhǒng qíngkuàng de fāshēng. Rúguǒ yǒu tuìchū xìnxī, zé bùyào bǎ suǒyǒu xìnxī dōu duīdié zài yì tiáo xiàn shàng, jintianweilai translation)

为了更好地理解它,请查看以下脚本,该脚本可以在不改变的情况下适用于不同的系统:

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

GOOD HABITS ARE BEAUTIFUL

  • 避免假设某个变量的值在一个非常遥远的时间内永远不会更改(特别是如果这样的错误会引发错误)。

  • 如果适用于您的代码,确保使用 trap 立即扩展变量的值。在单引号中传递给 trap 的任何变量名都将延迟其值的扩展,直到捕获之后。

  • 避免假设文件名不会包含任何空格。

  • 使用Bash ${VAR@Q}$(printf %q "$VAR")来解决文件名中的空格和其他特殊字符的问题,比如引号和回车符。

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

使用可预测的文件名与 $$ 的替代方法是一个巨大的安全漏洞,您永远不应该考虑使用它。即使是在您单用户 PC 上的简单个人脚本。这是一个非常不好的习惯,您不应该养成。 "BugTraq" 充满了 "不安全的临时文件" 事件。请参见 这里这里这里 以获取有关临时文件安全方面的更多信息。

我最初想引用不安全的TMP1和TMP2的任务,但经过再三考虑,那可能不是一个好主意。

我更喜欢使用tempfile,它以安全的方式在/tmp中创建文件,您无需担心其名称:

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




相关问题
热门标签