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

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

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

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

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

答案1

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

您可以通过打开文件的文件描述符并将其删除来确保在脚本退出(包括终止和崩溃)时删除该文件。/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)
# see section 2.7.6 of the POSIX definition of the Shell Command Language
exec 3>&-

答案2

用于mktemp创建临时文件。该实用程序返回所创建文件的完整路径。

temp_file=$(mktemp)

或者,创建一个临时的目录:

temp_dir=$(mktemp -d)

在脚本末尾,您可能想要删除临时文件或目录:

rm "${temp_file}"
rm -r "${temp_dir}"

注意:在目录或参数给定的目录中mktemp创建文件。有关其他选项以及如何以其他方式修改其行为,请参阅该实用程序的手册。/tmp--tmpdir

答案3

有些 shell 具有内置功能。

桀骜

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。

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

所以如果你这样做:

exec 3<<< test

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

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

cat <&3

如果在 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

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

POSIX

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

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 脚本中轻松执行此操作(zsh(sysseek内置) 和ksh93(<#((...))运算符) 可以尽管这样做)。

答案4

这是 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

相关内容