以下测试代码给出错误“错误的文件描述符”。为什么会这样?这只是一个测试代码,用于理解文件描述符及其与管道的交互。
示例1)
❯ echo "hello" 1>&2 1>&- |& echo "world"
world
zsh: 1: bad file descriptor
示例2)
echo "hello" 1>&2 1>&- | cat <<< "world"
zsh: write error: bad file descriptor
world
例3)
# Does not give error
❯ echo "helloworld" >&-
# Gives stdin? error
❯ cat <<< "helloworld" >&-
cat: stdin: Bad file descriptor
如果我只关闭 stderr,或同时关闭 stdout 和 stderr,那么我不会收到“错误文件描述符”错误。例如,以下两个都不会给出该错误:
❯ echo "hello" 1>&2 2>&- |& echo "world"
❯ echo "hello" 2>&1 2>&- |& echo "world"
❯ echo "hello" 1>&2 1>&- 2>&- |& echo "world"
答案1
|&
是2>&1 |
(如有记录的)。在执行其他重定向之前,左侧的标准输出连接到管道的写入侧,但标准错误在最后连接到当时的标准输出。如果标准输出被重定向,或者如您的示例所示,被关闭,这会产生影响。使用foo 1>&- |& bar
、 或等效的foo 1>&- 2>&1 | bar
、或者仅使用foo 1>&- 2>&1
,在应用 fd 2 到 fd 1 的重定向时,文件描述符 1 上没有任何内容。
使用 时foo 1>&2 1>&- | bar
,首先将 的标准输出foo
连接到管道的写入端,然后(假设multios
已打开)1>&2
创建一个到内置 T 型进程的管道,该进程将写入|
操作员创建的管道以及当时在文件描述符 2 上(即终端)。然后1>&-
关闭标准输出,因此如果foo
尝试写入标准输出,则此写入将失败。不管类似 tee 的进程写入多少个地方:在1>&-
它无法接收任何输入之后。
^我在文档中找不到指定的内容,但这就是 shell 传统上所做的事情,它是由 POSIX 指定。如果您不确定,您可以检查源代码、系统调用跟踪,或者您可以从各种重定向实验的结果(例如您一直在进行的实验)中推断出它。