要保存文件描述符,请在另一个fd上复制它。仅保存指向相应文件的路径是不够的,您需要保存打开模式,打开标志,文件中的当前位置等。当然,对于匿名管道或套接字,这是行不通的,因为它们没有路径。您要保存的是fd所引用的打开文件描述,而复制fd实际上是将新fd返回到相同的打开文件描述。
若要使用类似Bourne的shell将文件描述符复制到另一个文件描述符,语法为:
exec 3>&1
在上面,将fd 1复制到fd 3上。
之前已经开放的fd 3将会被关闭,但是请注意,fds 3至9(通常更多,最多有99个yash
)用于该目的(并且没有与0、1或2相反的特殊含义), Shell知道不将其用于自己的内部业务。事先打开fd 3的唯一原因是因为您在脚本1中进行了操作,或者它被调用方泄漏。
然后,您可以将stdout更改为其他内容:
exec > /dev/null
然后,恢复stdout:
exec >&3 3>&-
(3>&-
正在关闭不再需要的文件描述符)。
现在,问题在于,除了在ksh中,在此之后运行的每个命令都exec 3>&1
将继承fd3。这就是fd泄漏。通常来说没什么大不了的,但这会引起问题。
ksh
在那些fds 上设置close-on-exec标志(对于2以上的fds),但是其他外壳程序和其他外壳程序没有任何手动设置该标志的方法。
其他shell的解决方法是关闭每个命令的fd 3,例如:
exec 3>&-
exec > file.log
ls 3>&-
uname 3>&-
exec >&3 3>&-
笨拙的。在这里,最好的方法是根本不使用exec
,而是重定向命令组:
{
ls
uname
} > file.log
在那里,是shell负责保存stdout并随后将其还原(并且它是通过在fd上将其复制(设置为9,高于99,为yash
)在内部完成的,并设置了close-on-exec标志)。
注1
现在,如果您广泛地使用fds 3至9,或者在函数中使用它们,则这些fds 3至9的管理可能会变得麻烦且成问题,特别是如果您的脚本使用某些第三方代码,而这些第三方代码又会使用这些fds。
一些炮弹(zsh
,bash
,ksh93
,所有添加的功能(由奥利弗的Kiddle建议zsh
)在2005年大约在同一时间它自己的开发人员之间的讨论后)有特殊语法来分配高于10,而不是这有助于在这种情况下,第一自由FD:
myfunction() {
local fd
exec {fd}>&1
# stdout was duplicated onto a new fd above 10, whose actual value
# is stored in the fd variable
...
# it should even be safe to re-enter the function here
...
exec >&"$fd" {fd}>&-
}