我有一个 bash 脚本,它希望/dev/stdout
在用其他位置替换第一个文件描述符之前保留原始位置。
所以,很自然地,我写了类似的东西
old_stdout=$(readlink -f /dev/stdout)
但它不起作用。我很快就明白了问题所在:
test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18
显然,$()
在子 shell 中运行,该子 shell 通过管道传输到父 shell。
所以问题是:是否有一种可靠的(仅限于 Linux 发行版之间的可移植性)方法将/dev/stdout
位置保存为 bash 脚本中的字符串?
答案1
要保存文件描述符,请将其复制到另一个文件描述符上。保存相应文件的路径是不够的,您需要保存打开模式、打开标志、文件中的当前位置等。当然,对于匿名管道或套接字,这不起作用,因为它们没有路径。你想要保存的是打开文件描述fd 所引用的,复制一个 fd 实际上是将一个新的 fd 返回给同一个 fd打开文件描述。
要将文件描述符复制到另一个文件描述符上,使用类似 Bourne 的 shell,语法为:
exec 3>&1
上面,fd 1 被复制到 fd 3 上。
无论 fd 3 之前已经打开什么,都将被关闭,但请注意,fd 3 到 9(通常更多,最多 99 个yash
)是为此目的而保留的(并且没有与 0、1 或 2 相反的特殊含义), shell 知道不要将它们用于自己的内部业务。 fd 3 提前打开的唯一原因是您在脚本1中执行了它,或者它被调用者泄露了。
然后,您可以将 stdout 更改为其他内容:
exec > /dev/null
然后,恢复标准输出:
exec >&3 3>&-
(3>&-
关闭我们不再需要的文件描述符)。
现在的问题是,除了 ksh 之外,您之后运行的每个命令exec 3>&1
都将继承 fd 3。这就是 fd 泄漏。一般来说没什么大不了的,但这可能会导致问题。
ksh
设置执行时关闭这些 fd 上有标志(对于超过 2 个的 fd),但其他 shell 上没有标志,并且其他 shell 没有任何方法可以手动设置该标志。
其他 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 for yash
)在内部执行此操作执行时关闭标志设置)。
注1
现在,如果您广泛或在函数中使用这些 fds 3 到 9,那么它们的管理可能会很麻烦且有问题,特别是如果您的脚本使用一些可能反过来使用这些 fds 的第三方代码。
一些 shell ( zsh
, bash
, ksh93
, 都添加了该功能 (奥利弗·基德尔 (Oliver 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}>&-
}
答案2
$$
如果是交互式 shell 或编写相关 shell PID,则会获取当前进程 PID。
所以你可以使用:
readlink -f /proc/$$/fd/1
例子:
% readlink -f /proc/$$/fd/1
/dev/pts/33
% var=$(readlink -f /proc/$$/fd/1)
% echo $var
/dev/pts/33
答案3
正如您所看到的,bash 脚本不像常规编程语言那样可以分配文件描述符。
最简单的解决方案是使用子 shell 来运行您想要重定向的内容,以便可以将处理恢复到具有完整标准 I/O 的顶级 shell。
另一种解决方案是用于tty
识别 TTY 设备并控制脚本中的 I/O。例如:
dev=$(tty)
然后你就可以..
echo message > $dev