我知道您可以创建一个文件描述符并将输出重定向到它。例如
exec 3<> /tmp/foo # open fd 3.
echo a >&3 # write to it
exec 3>&- # close fd 3.
但是您可以在没有文件描述符的情况下执行相同的操作:
FILE=/tmp/foo
echo a > "$FILE"
我正在寻找一个很好的例子来说明何时必须使用额外的文件描述符。
答案1
大多数命令都有一个输入通道(标准输入,文件描述符 0)和一个输出通道(标准输出,文件描述符 1),或者对它们自己打开的多个文件进行操作(因此您向它们传递一个文件名)。 (这是标准错误(fd 2)的补充,它通常会一直过滤到用户。)但是,有时使用一个命令来充当来自多个源或多个目标的过滤器会很方便。例如,这是一个简单的脚本,用于将文件中的奇数行与偶数行分开
while IFS= read -r line; do
printf '%s\n' "$line"
if IFS= read -r line; then printf '%s\n' "$line" >&3; fi
done >odd.txt 3>even.txt
现在假设您想要对奇数行和偶数行应用不同的过滤器(但不将它们放回一起,这将是一个不同的问题,通常在 shell 中不可行)。在 shell 中,您只能将一个命令的标准输出通过管道传输到另一个命令;要通过管道传输另一个文件描述符,您需要先将其重定向到 fd 1。
{ while … done | odd-filter >filtered-odd.txt; } 3>&1 | even-filter >filtered-even.txt
另一个更简单的用例是过滤命令的错误输出。
exec M>&N
在脚本的其余部分将一个文件描述符重定向到另一个文件描述符(或者直到另一个此类命令再次更改文件描述符)。exec M>&N
和之间的功能有一些重叠somecommand M>&N
。该exec
形式更强大,因为它不必嵌套:
exec 8<&0 9>&1
exec >output12
command1
exec <input23
command2
exec >&9
command3
exec <&8
其他可能感兴趣的示例:
- “3>&1 1>&2 2>&3”在脚本中起什么作用?(它将 stdout 与 stderr 交换)
- 文件描述符和 shell 脚本
- 管道缓冲区有多大?
- Bash 脚本测试命令是否正确运行
还有更多的例子:
- 已标记的问题
io-redirection
- 已标记的问题
file-descriptors
- 在此网站上搜索示例在里面数据浏览器(Stack Exchange 数据库的公共只读副本)
PS 这是作者提出的一个令人惊讶的问题通过 fd 3 使用重定向的网站上获得最多支持的帖子!
答案2
下面是使用额外 FD 作为 bash 脚本闲聊控制的示例:
#!/bin/bash
log() {
echo $* >&3
}
info() {
echo $* >&4
}
err() {
echo $* >&2
}
debug() {
echo $* >&5
}
VERBOSE=1
while [[ $# -gt 0 ]]; do
ARG=$1
shift
case $ARG in
"-vv")
VERBOSE=3
;;
"-v")
VERBOSE=2
;;
"-q")
VERBOSE=0
;;
# More flags
*)
echo -n
# Linear args
;;
esac
done
for i in 1 2 3; do
fd=$(expr 2 + $i)
if [[ $VERBOSE -ge $i ]]; then
eval "exec $fd>&1"
else
eval "exec $fd> /dev/null"
fi
done
err "This will _always_ show up."
log "This is normally displayed, but can be prevented with -q"
info "This will only show up if -v is passed"
debug "This will show up for -vv"
答案3
在命名管道 (fifo) 的上下文中,使用附加文件描述符可以实现非阻塞管道行为。
(
rm -f fifo
mkfifo fifo
exec 3<fifo # open fifo for reading
trap "exit" 1 2 3 15
exec cat fifo | nl
) &
bpid=$!
(
exec 3>fifo # open fifo for writing
trap "exit" 1 2 3 15
while true;
do
echo "blah" > fifo
done
)
#kill -TERM $bpid
答案4
这是使用附加文件描述符似乎合适的另一种情况(在 Bash 中):
env -i bash --norc # clean up environment
set +o history
read -s -p "Enter your password: " passwd
exec 3<<<"$passwd"
mycommand <&3 # cat /dev/stdin in mycommand