我很清楚 Bash 中的文件描述符如何在实际水平上完成 I/O 重定向,但我在网上找到了一个案例(https://linuxcommand.org/lc3_adv_dialog.php,在“方法 2:使用命令替换和重定向”下),我不完全理解为什么使用它。
有一个执行需要将 stderr 重定向到 stdout 以捕获命令替换中的错误,这对我来说非常有意义,但是完成了一个额外的重定向,我认为需要了解文件描述符如何工作较低的水平。伪代码:
exec 3>&1
...
var=$(cmd -options ... 2>&1 1>&3)
...
exec 3>&-
备份标准输出并将标准输出重定向到它已经指向的位置有什么意义?除了现在接收 stderr 的内容之外,stdout 从未改变过。
我最好的猜测是 fd 3 包含 stdout 的原始状态,并且将 fd 1 重定向到 fd 3 将其返回到原始状态,然后再将 fd 2 重定向到它?我想要一些见解或阅读材料。
答案1
在您最初编写的形式中,命令只是cmd -options ... 2>&1 1>&3
,这没有什么意义,因为实际上 stderr 和 stdout 都被重定向到 stdout。所以1>&3
是不需要的。
但你提到的例子是另一种情况。我们所拥有的是variable=$(cmd -options ... 2>&1 1>&3)
。该$(command)
构造自行重定向 stdout command
,以便可以捕获它并将其放置在变量中。所以在这种情况下,第一个 stderrcommand
被重定向到已经重定向标准输出(2>&1
),即。将被放置在变量中,然后此 stdoutcommand
被重定向到原来的重定向前的 stdout,保存在描述符 3 ( 1>&3
) 中。效果是这样的标准错误将command
被放置在变量中并且标准输出将正常显示 - 与“常规”情况相反variable=$(command)
,其中 stdout 放置在变量中并显示 stderr。在您链接到的同一页面上对此进行了很好的解释:
乍一看,重定向似乎毫无意义。首先,我们使用 exec(更多重定向中介绍了这一点)将文件描述符 1 (stdout) 复制到描述符 3,以创建描述符 1 的备份副本。
下一步是执行命令替换并将对话框命令的输出分配给变量结果。该命令包括将描述符 2 (stderr) 重定向为描述符 1 的副本,最后,通过复制包含备份副本的描述符 3 将描述符 1 恢复为其原始值。可能不太明显的是为什么需要最后一次重定向。在子 shell 内,标准输出(描述符 1)不指向控制终端。相反,它指向一个将其内容传递给变量结果的管道。由于dialog需要标准输出指向终端,以便它可以显示输入框,因此我们必须将标准错误重定向到标准输出(以便dialog的输出最终出现在结果变量中),然后将标准输出重定向回控制终端。