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

Bhu*_*esh 243 shell-script tmp

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

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

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

Hau*_*ing 302

tmpfile=$(mktemp /tmp/abc-script.XXXXXX)
: ...
rm "$tmpfile"
Run Code Online (Sandbox Code Playgroud)

您可以通过打开文件的文件描述符并将其删除来确保在脚本退出(包括终止和崩溃)时删除文件。/proc/$PID/fd/$FD只要文件描述符处于打开状态,该文件就保持可用(对于脚本;不是真正用于其他进程,而是一种变通方法)。当它关闭时(当进程退出时内核会自动执行),文件系统会删除该文件。

# create temporary file
tmpfile=$(mktemp /tmp/abc-script.XXXXXX)

# create file descriptor 3 for writing to a temporary file so that
# echo ... >&3 writes to that file
exec 3>"$tmpfile"

# create file descriptor 4 for reading from the same file so that
# the file seek positions for reading and writing can be different
exec 4<"$tmpfile"

# delete temp file; the directory entry is deleted at once; the reference counter
# of the inode is decremented only after the file descriptor has been closed.
# The file content blocks are deallocated (this is the real deletion) when the
# reference counter drops to zero.
rm "$tmpfile"

# your script continues
: ...

# example of writing to file descriptor
echo foo >&3

# your script continues
: ...

# reading from that file descriptor
head -n 1 <&4

# close the file descriptor (done automatically when script exits)
exec 3>-
Run Code Online (Sandbox Code Playgroud)

  • 你如何从创建的FD中读取? (8认同)
  • `exec 3&gt; "$tmpfile"` 有什么作用?这不是只有在 tmpfile 是独立脚本时才有用吗? (7认同)
  • “你可以使用 cat &lt;3 或类似的东西。” 实际上,它是从名为 3 @dragon788 的文件中读取的。此外,`cat &lt;&amp;3` 将给出`Bad file descriptor`。如果您修复或删除它,我将不胜感激;错误信息没有多大帮助。 (6认同)
  • 好的答案,在崩溃时使用文件描述符的优雅解决方案 +1 (5认同)
  • @HaukeLaging 你会更新这个以显示如何从文件描述符中读取吗?自动删除您写入但不读取的文件并不是很好。 (4认同)
  • @DanielFarrell 我单独问了你的问题,[在这里得到了答案](/sf/ask/3880474671/)。 (4认同)
  • `/proc` - 除了没有它的系统。 (2认同)
  • @AlexejMagura 该命令创建了要使用的文件描述符,而不是(常规)文件路径以解决问题。 (2认同)
  • @eckes 您可以使用 `cat &lt;3` 或类似的东西。请记住 stdin 和 stdout 恰好位于 1 和 2 上,您可以轻松地将它们移动到其他描述符。 (2认同)

cha*_*aos 106

使用mktemp创建一个临时文件

temp_file=$(mktemp)
Run Code Online (Sandbox Code Playgroud)

或者,创建一个临时目录

temp_dir=$(mktemp -d)
Run Code Online (Sandbox Code Playgroud)

在脚本结束时,您必须删除临时文件或目录

rm ${temp_file}
rm -R ${temp_dir}
Run Code Online (Sandbox Code Playgroud)

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

  • 您可以在创建文件后立即使用 `trap "rm -f $temp_file" 0 2 3 15`,这样当脚本退出或使用 `ctrl-C` 停止时,文件仍会被删除。 (31认同)
  • 对于那些想知道的人,`trap "rm -f $temp_file" 0 2 3 15` 中的尾随整数是*信号* [运行第一个参数](https://www.shellscript.sh/trap.html )。0:退出外壳,2:中断,3:退出,15:终止。 (9认同)
  • @dragon788 你试过吗?你应该。`bash -c 'echo $$; 陷阱 "echo foo" 0; 睡5'` (6认同)
  • @HaukeLaging 然后,如果使用 Ctrl+C 停止脚本,则陷阱不会触发。需要注意的一点是,如果你 `kill -9 $somepid`,TRAP 没有帮助。那个特殊的杀戮信号是立即死亡,没有其他事情发生。 (4认同)
  • @wurtel 如果`EXIT` 是`trap` 的唯一钩子,会发生什么? (3认同)

Sté*_*las 25

某些外壳具有内置功能。

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
Run Code Online (Sandbox Code Playgroud)

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

Linux 上的 bash/zsh。

bash5.1 之前版本中的here-documents 或 here-strings并zsh作为已删除的临时文件实现(就像在 70 年代后期引入 here-documents 的 Bourne shell 中的情况一样)。

所以如果你这样做:

exec 3<<< test
Run Code Online (Sandbox Code Playgroud)

文件描述符 3 连接到一个已删除的临时文件,其中包含test\n.

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

cat <&3
Run Code Online (Sandbox Code Playgroud)

如果在 Linux 上,您也可以通过 读取或写入该文件/dev/fd/3,但使用 bash 5.0 版,您首先需要恢复对其的写入权限(bash 在该版本中明确删除了该权限):

$ exec 3<<< test
$ cat <&3
test
$ chmod u+w /dev/fd/3 # only needed in bash 5.0
$ echo foo > /dev/fd/3
$ cat /dev/fd/3
foo
Run Code Online (Sandbox Code Playgroud)

(其他一些 shell 使用管道,或者/dev/null如果此处的文档为空,则可能会使用)。

POSIX

没有mktempPOSIX 实用程序。然而,POSIX 指定了一个mkstemp(template)C APIm4标准实用程序使用mkstemp()同名的m4 函数公开该 API 。

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

所以,你可以这样做:

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

但是请注意,您需要在退出时处理清理工作,但如果您只需要写入和读取文件固定次数,您可以在为 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
Run Code Online (Sandbox Code Playgroud)

您可以打开文件进行一次读取,然后在两次读取之间倒带,但是没有 POSIX 实用程序可以执行倒带 ( lseek()),因此您不能在 POSIX 脚本 ( zsh(sysseek内置) 和ksh93(<#((...))运算符) 中可移植地执行此操作做吧)。

  • @WinnieNicklaus,是的,但这不使用临时文件,所以在这里无关紧要。进程替换由 ksh 引入,由 bash 和 zsh 复制,zsh 将其扩展为第三种形式:`=(...)`。 (4认同)
  • Bash 还可以使用 `&lt;()` 进行进程替换 (2认同)

cuo*_*glm 18

如果您在具有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"
Run Code Online (Sandbox Code Playgroud)

  • `mktemp` 起源于具有不同语法的 HP/UX。Todd C. Miller 在 90 年代中期为 OpenBSD 创建了一个不同的(由 FreeBSD 和 NetBSD 复制),后来也将其作为独立实用程序提供(www.mktemp.org)。在 2007 年将一个(大部分兼容的)`mktemp` 实用程序添加到 GNU coreutils 之前,它通常在 Linux 上使用。只是说不能真正说 `mktemp` 是一个 GNU 实用程序。 (4认同)

Swa*_*anS 13

以下是 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
Run Code Online (Sandbox Code Playgroud)

  • 需要注意的是,内容只能使用一次。即,如果我第二次执行 cat &lt;&amp;$FD_R ,则不会产生任何输出。请参阅 https://unix.stackexchange.com/questions/166482/cat-stdin-wont-work-twice-in-script/166485。如果程序崩溃,有没有办法让文件自动删除,但可以多次访问? (3认同)