在运行脚本时,我想在/tmp目录中创建一个临时文件。执行该脚本后,该脚本将清除该文件。

如何执行该操作在shell脚本中?

#1 楼

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


评论


好的答案,当文件崩溃+1时提供带有文件描述符的优雅解决方案

–混乱
2015年1月30日7:29



exec 3>“ $ tmpfile”的作用是什么?如果tmpfile是独立脚本,这不仅有用吗?

–́Alexej Magura
16年11月29日在18:36



您如何从创建的FD中读取?

–eckes
17年4月7日在9:37

“您可以使用cat <3或类似的名称。”实际上是从名为3 @ dragon788的文件中读取的。同样,cat <&3将提供错误的文件描述符。如果您修复或删除它,我将不胜感激。错误信息并没有太大帮助。

–丹尼尔·法瑞尔(Daniel Farrell)
19年2月21日在5:24



@DanielFarrell我单独询问了您的问题,并在这里得到了答案。

–乔纳
19年3月31日4:27在

#2 楼

使用mktemp创建一个临时文件

temp_file=$(mktemp)


,或创建一个临时目录:

temp_dir=$(mktemp -d)


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

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


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

评论


您可以在创建文件后立即使用陷阱“ rm -f $ temp_file” 0 2 3 15,以便在脚本退出或使用ctrl-C停止时,仍将删除该文件。

–Wurtel
2015年1月30日7:27



@wurtel如果EXIT是陷阱的唯一钩子怎么办?

– Hauke Laging
15年1月30日在7:41

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

–dragon788
17年7月21日在22:08

@ dragon788您是否尝试过?你应该。 bash -c'echo $$;陷阱“回声富”​​ 0;睡5'

– Hauke Laging
17年8月5日在6:22

对于那些想知道的人,陷阱“ rm -f $ temp_file” 0 2 3 15中的结尾整数是运行第一个参数的信号。 0:退出外壳程序; 2:中断; 3:退出; 15:终止。

– ijoseph
20-5-23在18:41



#3 楼

有些shell具有内置的功能。

zsh

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中的此处文件或here-strings被实现为已删除的临时文件。

因此,如果这样做:

exec 3<<< test


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

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

cat <&3


如果在Linux上,您还可以通过/dev/fd/3读取或写入该文件,尽管在bash版本5及更高版本中,您首先需要恢复对其的写入权限(其中bash现在已显式删除):

$ exec 3<<< test
$ cat <&3
test
$ chmod u+w /dev/fd/3 # only needed in bash 5+
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo


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

POSIX

没有mktemp POSIX实用程序。 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-string方法那样在创建后立即将其打开并删除:

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脚本(zsh(内置sysseek)和ksh93)中进行移植(<#((...))操作员)可以做到)。

评论


Bash也可以使用<()进行进程替换

– WinnieNicklaus
15年1月30日在20:45

@WinnieNicklaus,是的,但是它不使用临时文件,因此在这里无关紧要。进程替换由ksh引入,由bash和zsh复制,zsh用第三种形式扩展它:=(...)。

–StéphaneChazelas
15年1月30日在21:01

#4 楼

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

使用POSIX工具箱:

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


评论


如果EXIT是唯一的陷阱,会发生什么?

– Hauke Laging
15年1月30日在7:40

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

–cuonglm
2015年1月30日8:00



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

– Hauke Laging
15年1月30日在8:03

@HaukeLaging:你是什么意思,那不是什么事?

–cuonglm
15年1月30日在8:05

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

–StéphaneChazelas
15年1月30日在22:01

#5 楼

在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


评论


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

– smihael
17年8月24日在22:09



#6 楼

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

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


,以便可以像

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


那样使用它我喜欢在随机值之前格式化日期时间的原因是,它使我能够轻松找到刚创建的tmp文件,而且我也不必考虑下次要命名的内容(而只需要专注于将我的dang脚本保存到工作)。