如何在shell脚本中创建一个临时文件?


155

在运行脚本时,我想在/tmp目录中创建一个临时文件。

执行完该脚本后,该脚本将清除该脚本。

如何在shell脚本中做到这一点?

Answers:


198
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
: ...
rm "$tmpfile"

通过打开文件描述符并将其删除,可以确保在脚本退出(包括杀死和崩溃)时删除文件。/proc/$PID/fd/$FD只要打开文件描述符,文件就保持可用(对于脚本;不是真的对其他进程有效,但是可以解决)。当它关闭(内核在进程退出时自动执行)时,文件系统删除该文件。

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
exec 3>"$tmpfile"
rm "$tmpfile"
: ...
echo foo >&3

4
好的答案,当文件崩溃+1时使用文件描述符的优雅解决方案
混乱

2
/proc-除了没有它的系统。
丹尼斯·威廉姆森

4
怎么exec 3> "$tmpfile"办?仅当tmpfile是独立脚本时,这才有用吗?
Alexej Magura

5
您如何从创建的FD中读取?
eckes'Apr

3
“您可以使用cat <3或类似的名称。” 实际上是从名为3 @ dragon788的文件中读取的。另外,cat <&3会给Bad file descriptor。如果您修复或删除它,我将不胜感激。错误信息并没有太大帮助。
Daniel Farrell

65

使用mktemp创建一个临时文件或目录:

temp_file=$(mktemp)

或直接使用:

temp_dir=$(mktemp -d)

在脚本末尾,您必须删除临时文件/目录:

rm ${temp_file}
rm -R ${temp_dir}

mktemp /tmp--tmpdir参数指定的目录或目录中创建文件。


20
您可以trap "rm -f $temp_file" 0 2 3 15在创建文件后立即使用,以便在脚本退出或停止运行时ctrl-C仍将其删除。
wurtel

1
@wurtel如果EXIT唯一的钩子会发生什么trap
Hauke Laging

4
@HaukeLaging如果脚本用Ctrl + C停止,陷阱不会触发。需要注意的一点是,TRAP对您没有帮助kill -9 $somepid。那个特殊的杀手信号是致命的,什么也没有发生。
dragon788

5
@ dragon788您是否尝试过?你应该。bash -c 'echo $$; trap "echo foo" 0; sleep 5'
Hauke Laging,

诱捕EXIT就足够了。
库萨兰南达

15

如果您使用的系统具有mktemp,则应将其用作其他答案。

使用POSIX工具箱:

umask 0177
tmpfile=/tmp/"$0"."$$"."$(awk 'BEGIN {srand();printf "%d\n", rand() * 10^10}')"
trap 'rm -f -- "$tmpfile"' INT TERM HUP EXIT
: > "$tmpfile"

如果EXIT唯一的钩子会发生什么trap
Hauke Laging

@HaukeLaging:tmpfile仍然可以在脚本退出之前删除,但是在脚本收到其他信号时不会删除。
cuonglm

这不是这里发生的情况(GNU bash,版本4.2.53)。
Hauke Laging

@HaukeLaging:你是什么意思That's not what happens
cuonglm

3
mktemp源于HP / UX,具有不同的语法。Todd C. Miller在90年代中期为OpenBSD创建了另一种版本(由FreeBSD和NetBSD复制),后来又以独立实用程序的形式提供(www.mktemp.org)。那是在Linux上通常使用的,直到2007年将一个(几乎兼容的)mktemp实用程序添加到GNU coreutils 为止。说的不是一个真正mktemp的GNU实用程序。
斯特凡Chazelas

14

一些外壳程序具有内置功能。

sh

zsh=(...)过程替代形式是使用一个临时文件。例如,=(echo test)扩展到包含的临时文件的路径test\n

$ {cat $file; ls -l /dev/fd/3; echo test2 >&3; cat $file} 3<> ${file::==(echo test)}
test
lrwx------ 1 stephane stephane 64 Jan 30 11:19 /dev/fd/3 -> /tmp/zshMLbER0
test2

命令完成后,该文件将自动删除。

Linux上的bash / zsh。

在这里,文件或点击这里,字符串bashzsh实现为删除临时文件。

因此,如果您这样做:

exec 3<<< test

文件描述符3连接到包含的已删除临时文件test\n

您可以通过以下方式获取其内容:

cat <&3

如果在Linux上,您还可以通过以下方式对该文件进行读写 /dev/fd/3

$ exec 3<<< test
$ cat <&3
test
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo

(其他一些shell使用管道,或者/dev/null如果here文档为空,则可以使用)。

POSIX

没有mktempPOSIX实用程序。但是,POSIX指定了mkstemp(template)C API,并且m4标准实用程序使用mkstemp()相同名称的m4函数公开该API 。

mkstemp()为您提供了一个文件名,该文件名具有随机部分,可以保证在调用函数时该部分不存在。它确实以无竞争的方式创建了权限为0600的文件。

因此,您可以执行以下操作:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

但是请注意,您需要在退出时进行清理,尽管如果您只需要读写固定次数的文件,则可以像创建here-doc / here-那样在创建后立即将其打开并删除。上面的字符串方法:

tmpfile=$(
  echo 'mkstemp(template)' |
    m4 -D template="${TMPDIR:-/tmp}/baseXXXXXX"
) || exit

# open once for writing, twice for reading:
exec 3> "$tempfile" 4< "$tempfile" 5< "$tempfile"

rm -f -- "$tmpfile"

cmd >&3   # store something in the temp file
exec 3>&- # fd no longer needed

# read the content twice:
cat <&4
cat <&5

您可以打开文件以读取一次,然后在两次读取之间进行倒带,但是没有POSIX实用程序可以进行倒带(lseek()),因此您无法在POSIX脚本中进行可移植的操作(zshsysseek内置)和ksh93<#((...))运算符)尽管这样做)。


1
Bash还可以使用<()
WinnieNicklaus 2015年

3
@WinnieNicklaus,是的,但这不使用临时文件,因此与此处无关。进程替换由ksh引入,由bash和zsh复制,zsh用第3种形式扩展了它=(...)
斯特凡Chazelas

7

在Hauke Laging的产品线中,以下是一些改进的答案:

#!/bin/bash

tmpfile=$(mktemp)  # Create a temporal file in the default temporal folder of the system

# Lets do some magic for the tmpfile to be removed when this script ends, even if it crashes
exec {FD_W}>"$tmpfile"  # Create file descriptor for writing, using first number available
exec {FD_R}<"$tmpfile"  # Create file descriptor for reading, using first number available
rm "$tmpfile"  # Delete the file, but file descriptors keep available for this script

# Now it is possible to work with the temporal file
echo foo >&$FD_W
echo bar >&$FD_W  # Note that file descriptor always concatenates, not overwrites

cat <&$FD_R

2
应该注意的是,该内容只能使用一次。即,如果我第二次执行cat <&$ FD_R,则不会产生任何输出。参见unix.stackexchange.com/questions/166482/…。如果程序崩溃,有什么方法可以自动删除文件,但是可以多次访问它?
smihael

0

我通常使用临时文件的工作流程是因为我正在测试某些bash脚本。我想要完成tee它,以便可以看到它正在工作,并将输出保存到我​​的过程的下一个迭代中。我创建了一个名为tmp

#!/bin/bash
echo $(mktemp /tmp/$(date +"%Y-%m-%d_%T_XXXXXX"))

这样我就可以像

$ some_command --with --lots --of --stuff | tee $(tmp)

我喜欢将日期时间格式化为随机值之前的原因是,它使我能够轻松找到刚创建的tmp文件,并且不必考虑下次要命名的内容(而只需要获取我的dang脚本即可)上班)。

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.