如果你想用 Bash 覆盖一个文件,这很容易
echo "Hello world" > hosts
这似乎不适用于文件描述符
$ exec 3<> hosts
$ echo "Hello world" >&3
$ cat hosts
Hello world
$ echo "Hello world" >&3
$ cat hosts
Hello world
Hello world
如果你想用 Bash 覆盖一个文件,这很容易
echo "Hello world" > hosts
这似乎不适用于文件描述符
$ exec 3<> hosts
$ echo "Hello world" >&3
$ cat hosts
Hello world
$ echo "Hello world" >&3
$ cat hosts
Hello world
Hello world
这是正确的。当 shell 调用open(2)
. 当您DUP2
使用 FD(任何语言)时,打开文件时设置的标志在打开的 FD之间共享。在您的情况下,O_TRUNC
只能在实际打开文件时指定。
<file
要知道的重要一点是,模式和各种标志仅在使用、>file
或类似命令打开文件时才确定。复制带有&
修饰符的 FD 实质上会创建一个指向原始 FD 的“别名”,并保留与原始 FD 相同的所有状态。截断文件需要重新打开它。
如果您想轻松使用文件描述符,这是我的调试功能:
lsfd() {
local ofd=${ofd:-2} target=${target:-$BASHPID}
while [[ $1 == -* ]]; do
if [[ -z $2 || $2 == *[![:digit:]]* ]]; then
cat
return 1
fi
case ${1##+(-)} in
u)
shift
ofd=$1
shift
;;
t)
shift
target=$1
shift
;;
h|\?|help)
cat
return
esac
done <<EOF
USAGE: ${FUNCNAME} [-h|-?|--help] [-u <fd>] [ -t <PID> ] [<fd1> <fd2> <fd3>...]
This is a small lsof wrapper which displays the open
file descriptors of the current BASHPID. If no FDs are given,
the default FDs to display are {0..20}. ofd can also be set in the
environment.
-u <fd>: Use fd for output. Defaults to stderr. Overrides ofd set in the environment.
-t <PID>: Use PID instead of BASHPID. Overrides "target" set in the environment.
EOF
IFS=, local -a 'fds=('"${*:-{0..20\}}"')' 'fds=("${fds[*]}")'
lsof -a -p $target -d "$fds" +f g -- >&${ofd}
}
我喜欢不关闭标准输入,因为这有时会导致问题,所以首先保存它。
$ ( { lsfd 3; cat <&3; } {savefd}<&0 <<<'hi' 3>&0- <&"${savefd}" )
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME
bash 920 ormaaj 3r REG LG 0,22 3 59975426 /tmp/sh-thd-8305926351 (deleted)
hi
如您所见,即使使用3>&0-
运算符将 FD 0 移动到 3,文件仍保持打开状态O_RDONLY
。复制描述符的选择>
or<
是任意的,并且仅在省略运算符左侧的 FD 时才用于确定默认值。
如果您确实想打开一个新的独立 FD,可以这样做:
$ ( {
cat <&4 >/dev/null; lsfd 3 4; echo there >&4; cat </dev/fd/3
} {savefd}<&0 <<<'hi' 3>&0- 4<>/dev/fd/3 <&"${savefd}"
)
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME
bash 2410 ormaaj 3r REG LG 0,22 3 59996561 /tmp/sh-thd-8305914274 (deleted)
bash 2410 ormaaj 4u REG RW,LG 0,22 3 59996561 /tmp/sh-thd-8305914274 (deleted)
hi
there
现在 FD 4 真正“重新打开”到指向 FD 3 的文件,而不仅仅是复制(即使该文件已经被unlink(2)
'd,如上所述)。首先打开 FD 4 并使用 搜索到末尾cat
,然后将附加行写入文件。同时 FD 3 的查找位置仍然在开头,所以整个生成的文件可以cated 到终端。
同样的原则也适用于你的情况。
$ { echo 'Hello world'; echo 'hi' >/dev/fd/1; } >hosts; cat hosts
hi
或者可能更好的是使用两个单独的命令打开和关闭文件两次,而不使用exec
.
除非绝对必要,否则我宁愿避免exec
仅使用打开文件描述符。您必须记住明确关闭文件。如果您改用命令分组,文件将自动关闭,实际上给它们一个“范围”。这在原理上类似于 Python 的with
声明。这可能避免了一些混乱。
另见: