如果你想用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
答案 0 :(得分:5)
这是对的。 shell调用open(2)
时确定打开文件的模式。当您DUP2
FD(使用任何语言)时,打开文件时设置的标志为shared between open FDs。在您的情况下,O_TRUNC
只能在文件实际打开时指定。
重要的是要知道只有在使用<file
,>file
或类似文件打开文件时才会确定模式和各种标志。使用&
修饰符复制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}
}
我喜欢不关闭stdin,因为这有时会导致问题,所以它会先被保存。
$ ( { 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
。 >
或<
的选择对于复制描述符是任意的,只有在省略运算符左侧的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的搜索位置仍处于开头,因此整个结果文件可以被设置为终端。
同样的原则可适用于您的案件。
$ { echo 'Hello world'; echo 'hi' >/dev/fd/1; } >hosts; cat hosts
hi
或者更好的方法是使用两个单独的命令打开和关闭文件两次,而不是exec
。
除非绝对必要,否则我宁愿避免使用exec
来打开文件描述符。您必须记住明确关闭该文件。如果使用命令分组,文件将自动关闭,实际上为它们提供了“范围”。这与Python的with
语句原则上类似。这可能会避免一些混乱。
另见: