什么时候会使用额外的文件描述符?

什么时候会使用额外的文件描述符?

我知道您可以创建一个文件描述符并将输出重定向到它。例如

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

其他可能感兴趣的示例:

还有更多的例子:

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 中):

命令行参数的Shell脚本密码安全

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

相关内容