在 bash 中创建临时文件

在 bash 脚本中创建临时文件客观上有更好的方法吗?

我通常只是将它们命名为任何我想到的名字,比如 temfile-123,因为当脚本结束时它将被删除。除了在当前文件夹中覆盖可能的 temfile-123之外,这样做还有什么缺点吗?或者以更仔细的方式创建临时文件有什么好处吗?

124164 次浏览

你可能想看看 mktemp

Mktemp 实用程序接受给定的文件名模板并覆盖 部分来创建唯一的文件名。模板可以是任何 文件名后面附加了一定数量的“ X”,例如 /tmp/tfile.XXXXXXXXXX。尾随的“ X”被替换为当前进程编号和随机字母的组合。

详情请浏览: Man Mktemp

是的,使用 Mktemp

它将在用于存储临时文件的文件夹中创建一个临时文件,并保证您有一个唯一的名称。它输出该文件的名称:

> mktemp
/tmp/tmp.xx4mM3ePQY
>

mktemp(1)的手册页非常好地解释了这一点:

传统上,许多 shell 脚本使用程序的名称 将 pid 作为后缀,并将其用作临时文件名 命名方案的竞争条件是可预测的,它创建的竞争条件是 攻击者很容易获胜。一个更安全,但仍然劣势的方法 是使用相同的命名方案创建一个临时目录 这确实可以保证临时文件不会 它仍然允许一个简单的分布式拒绝服务攻击攻击 由于这些原因,建议使用 mktemp。

在脚本中,我调用 mktemp

mydir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename $0).XXXXXXXXXXXX")

它创建了一个临时目录,我可以在其中工作,我可以安全地命名实际文件的可读性和有用的东西。

mktemp并不是标准的,但它确实存在于许多平台上。“ X”通常会被转换成某种随机性,更多的可能是更随机的; 然而,一些系统(例如 busybox ash)比其他系统更明显地限制了这种随机性


顺便说一下,安全创建临时文件不仅仅是 shell 脚本编程,它还很重要。这就是为什么 python 有 Tempfile,perl 有 文件: : 临时工,ruby 有 临时档案等等。.

以更仔细的方式创建临时文件有什么好处吗

临时文件通常在临时目录(比如 /tmp)中创建,其中所有其他用户和进程都具有读写权限(任何其他脚本都可以在此创建新文件)。因此,脚本在创建文件时应该小心,比如使用正确的权限(例如,只为所有者读,参见: help umask) ,文件名不应该容易猜测(理想情况下是随机的)。否则,如果文件名不是唯一的,它可能会与多次运行的同一个脚本产生冲突(例如 比赛状态) ,或者一些攻击者可能会劫持一些敏感信息(例如当权限过于开放和文件名很容易猜测时) ,或者创建/替换文件,使用他们自己的代码版本(例如根据存储的内容替换命令或 SQL 查询)。


您可以使用以下方法来创建临时目录:

TMPDIR=".${0##*/}-$$" && mkdir -v "$TMPDIR"

或临时档案:

TMPFILE=".${0##*/}-$$" && touch "$TMPFILE"

然而,它仍然是可预测的,并不被认为是安全的。

根据 man mktemp,我们可以读到:

传统上,许多 shell 脚本使用带有 pid 的程序名作为后缀,并将其用作临时文件名。这种命名方案是可预测的,它创建的竞争条件很容易让攻击者获胜。

因此,为了安全起见,建议使用 mktemp命令创建唯一的临时文件或目录(-d)。

mktemp可能是最通用的,特别是如果您打算使用该文件一段时间。

如果您只是暂时需要该文件作为另一个命令的输入,也可以使用 进程替换运算符进程替换运算符 <(),例如:

$ diff <(echo hello world) <(echo foo bar)

mktemp文件有一些很好的例子。

如果临时文件需要某个后缀(文件扩展名) ,可以执行以下操作

$ myfile=$(mktemp --suffix ".txt")
$ echo "$myfile"
/tmp/tmp.9T9soL2QNp.txt

如果您不希望创建文件,但只想要一个名称,您可以另外使用 -u/--dry-run标志。

$ myfile=$(mktemp -u --suffix ".txt")
$ echo "$myfile"
/tmp/tmp.Y8cMDJ1DDr.txt

当使用 -u/--dry-run时,请注意

使用此命令的输出创建新文件本质上是不安全的,因为在生成名称和使用名称之间存在一个时间窗口,其他进程可以使用相同的名称创建对象。

为了扩展以前的答案,这里需要运行 mktemp 还有,确保之后也进行了清理。通常的方法是使用 trap,它允许您设置一个可以在脚本中断时运行的钩子。

Bash 还提供了 EXIT伪信号,这样您就可以设置一个 trap在脚本成功退出时运行,而 ERR在脚本产生错误时触发。(另请参阅 Set-e 在 bash 脚本中是什么意思?了解一些不明显的后果。)

t=$(mktemp -d -p temporary.XXXXXXXXXXXX) || exit
trap 'rm -rf "$t"; exit' ERR EXIT  # HUP INT TERM


: # use "$t" to your heart's content ...

您可能希望在 ERREXIT之外设置其他信号; 显然,kill -9不能被捕获(这就是为什么不应该使用它,除非在紧急情况下)。HUP(信号1)和 INT(信号2)分别在脚本会话挂起或用户按 ctrl-C 时生成。TERM(信号15)是 kill发送的默认信号,请求终止脚本。

mktemp -p取代了被认为已经过时的 mktemp -t-d选项指定创建一个目录; 如果您只需要一个临时文件,显然,这是不必要的。