Answers:
3>&4-
是bsh也支持的ksh93扩展,它是的缩写3>&4 4>&-
,即3现在指向4过去的位置,而4现在已关闭,因此4所指向的内容现在移至3。
典型的用法是在您复制stdin
或stdout
保存它的副本并想要还原它的情况下,例如:
假设您要捕获命令的stderr(仅stderr),而将stdout留在变量中。
命令替换var=$(cmd)
,创建管道。管道的写入端变为cmd
stdout(文件描述符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
除非命令对此加以防范!也不是我们想要的。
如果我们希望cmd
单独保留stdout ,即指向它在命令替换外部指向的相同资源,那么我们需要以某种方式将该资源带入命令替换内部。为此,我们可以在命令替换stdout
外部进行复制以将其放入内部。
{
var=$(cmd)
} 3>&1
哪种写方法更干净:
exec 3>&1
var=$(cmd)
exec 3>&-
(这还具有还原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-
然后变成:
现在,我们有了所需的东西:stderr进入管道,stdout保持不变。但是,我们将fd 3泄漏给cmd
。
尽管命令(按照惯例)假定fds 0到2是打开的并且是标准输入,输出和错误,但它们不假定其他任何fds。他们很可能会保留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
其中,具有bash
可缩短到:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
总结一下为什么很少使用它的原因:
>&3
代替>&3-
or即可>&3 3>&-
。正如您发现的那样,它很少使用的证明是bash虚假的。在bash compound-command 3>&4-
或any-builtin 3>&4-
树叶的fd 4即使关闭compound-command
或any-builtin
返回。一个补丁来解决这个问题,现在(2013年2月19日)提供。
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
收盘1是错字吗?
$(...)
)。
{...}
,fd 3指向fd 1以前指向的位置,并且fd 1关闭,然后在进入时$(...)
,fd 1设置为进给的管道$var
,然后也将其设置为cmd
2,然后再将1指向3点,即外部1。事后b仍然关闭的事实是bash中的错误,我将进行报告。该功能来自的ksh93没有该错误。
这意味着要使其指向其他文件描述符所在的相同位置。你需要这样做很少,除了标准错误描述的明显不同的处理(stderr
,fd 2
,/dev/stderr -> /proc/self/fd/2
)。在某些复杂的情况下,它可以派上用场。
Advanced Bash Scripting指南包含以下较长的日志级别示例和以下代码段:
# Redirecting only stderr to a pipe.
exec 3>&1 # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&- # Close fd 3 for 'grep' (but not 'ls').
# ^^^^ ^^^^
exec 3>&- # Now close it for the remainder of the script.
例如,在Source Mage的法术中,我们使用它来识别同一代码块的不同输出:
(
# everything is set, so run the actual build infrastructure
run_build
) 3> >(tee -a $C_LOG >> /dev/stdout) \
2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
> >(tee -a $C_LOG > $VOYEUR_STDOUT)
由于日志记录的原因,它附加了额外的过程替代项(VOYEUR决定是将数据显示在屏幕上还是仅记录日志),但是始终需要显示一些消息。为此,我们将它们打印到文件描述符3中,然后对其进行特殊处理。
在Unix中,文件由文件描述符处理(较小的整数,例如,标准输入为0,标准输出为1,标准错误为2;当您打开其他文件时,通常会为它们分配最小的未使用描述符)。因此,如果您知道该程序的内幕,并且想将文件描述符5的输出发送到标准输出,则可以将描述符5移到1。这就是错误的2> errors
来源,结构喜欢2>&1
将错误复制到输出流。
因此,几乎从未使用过(我隐约记得在我25年以上几乎完全使用Unix的情况下一次或两次愤怒地使用它),但是在需要时绝对必要。
5>&1
将5发送到1会去的地方,那么1>&5-
除了关闭5之外,究竟会做什么?