Que*_*tin 16 bash io-redirection file-descriptors
根据 bash 手册页:
重定向运算符
Run Code Online (Sandbox Code Playgroud)[n]<&digit-如果未指定,则将文件描述符移动
digit到文件描述符n或标准输入(文件描述符 0)n。digit复制到 后关闭n。
将文件描述符“移动”到另一个是什么意思?这种做法的典型情况是什么?
Sté*_*las 14
3>&4-是 bash 也支持的 ksh93 扩展,它是 的缩写3>&4 4>&-,即 3 现在指向 4 以前的位置,而 4 现在已关闭,因此 4 指向的内容现在已移至 3。
典型用法是在您复制stdin或stdout保存它的副本并希望恢复它的情况下,例如:
假设您想捕获命令的 stderr(并且仅限于 stderr),而将 stdout 单独留在变量中。
命令替换var=$(cmd),创建一个管道。管道的写入端成为cmd的标准输出(文件描述符 1),而另一端由 shell 读取以填充变量。
现在,如果你想要stderr去的变量,你可以这样做:var=$(cmd 2>&1)。现在 fd 1 (stdout) 和 2 (stderr) 都进入管道(并最终进入变量),这只是我们想要的一半。
如果我们这样做var=$(cmd 2>&1-)( 的缩写var=$(cmd 2>&1 >&-),现在只有cmd的 stderr 进入管道,但 fd 1 已关闭。如果cmd尝试写入任何输出,将返回一个EBADF错误,如果它打开一个文件,它将获得第一个空闲 fd 并且打开的文件将被分配给它,stdout除非命令对此进行防范!也不是我们想要的。
如果我们想让 stdout ofcmd保持不变,即指向它在命令替换之外指向的相同资源,那么我们需要以某种方式将该资源引入命令替换内。为此,我们可以在命令替换stdout 之外进行复制以将其带入。
{
var=$(cmd)
} 3>&1
Run Code Online (Sandbox Code Playgroud)
这是一种更简洁的写法:
exec 3>&1
var=$(cmd)
exec 3>&-
Run Code Online (Sandbox Code Playgroud)
(这也有恢复 fd 3 而不是最后关闭它的好处)。
然后在{(或exec 3>&1) 直到}, fd 1 和 3 都指向最初指向的相同资源 fd 1 。fd 3 还将指向命令替换中的该资源(命令替换仅重定向 fd 1,stdout)。所以在上面,对于cmd,我们有 fds 1、2、3:
如果我们将其更改为:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Run Code Online (Sandbox Code Playgroud)
然后就变成了:
现在,我们得到了我们想要的东西:stderr 进入管道,stdout 保持不变。但是,我们正在将 fd 3 泄漏到cmd.
虽然命令(按照惯例)假定 fds 0 到 2 是打开的并且是标准输入、输出和错误,但它们不假定其他 fds 的任何内容。他们很可能会保留那个 fd 3 不变。如果他们需要另一个文件描述符,他们将只执行open()/dup()/socket()...返回第一个可用文件描述符的操作。如果(就像一个 shell 脚本那样exec 3>&1)他们需要fd专门使用它,他们将首先将它分配给某个东西(并且在该过程中,我们的 fd 3 持有的资源将被该过程释放)。
关闭那个 fd 3 是一种很好的做法,因为cmd它没有使用它,但是如果我们在调用cmd. 问题可能是:(cmd以及可能产生的其他进程)可用的 fd 少了一个。一个潜在的更严重的问题是,该 fd 指向的资源是否可能最终被cmd后台产生的进程所持有。如果该资源是管道或其他进程间通信通道(例如当您的脚本作为 运行时script_output=$(your-script)),则可能会令人担忧,因为这意味着从另一端读取的进程将永远不会看到文件结束,直到后台进程终止。
所以在这里,最好这样写:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Run Code Online (Sandbox Code Playgroud)
其中,withbash可以缩短为:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Run Code Online (Sandbox Code Playgroud)
总结一下很少使用的原因:
>&3代替>&3-or >&3 3>&-。证明它很少使用,正如您发现的那样,它在 bash 中是伪造的。在 bashcompound-command 3>&4-或any-builtin 3>&4-离开 fd 4 之后compound-command或any-builtin已经返回。一个补丁来解决这个问题,现在(2013年2月19日)提供。