【发布时间】:2019-02-10 01:43:38
【问题描述】:
我一直在寻找一种方法来使用 Bash 间接将所有输出(1 (STDOUT)、2 (STDERR)、3 等)重新路由到命名管道。这是我为测试该理论而编写的脚本:
#!/bin/bash
pipe1="/tmp/pipe1"
pipe2="/tmp/pipe2"
pipe3="/tmp/pipe3"
mkfifo "${pipe1}"
mkfifo "${pipe2}"
mkfifo "${pipe3}"
trap "rm -rf ${pipe1} ${pipe2} ${pipe3}" EXIT
printer() {
echo "OUT" >&1
echo "ERR" >&2
echo "WRN" >&3
}
# Usage: mux
mux() {
cat "${pipe1}"
cat "${pipe2}"
cat "${pipe3}"
}
printer 1>"${pipe1}" 2>"${pipe2}" 3>"${pipe3}"
mux
这段代码似乎没问题,但终端无限期挂起,直到它被终止。据我了解,管道就像文件一样,它们有一个 inode,但它们不是写入磁盘,而是写入内存。
话虽如此,它应该像任何其他文件一样可以访问。我知道脚本挂在调用打印机函数的行上。我还测试了几种子shell 组合和更高级的重定向(即重定向到STDOUT 以处理其他每个管道)。也许我在命名管道中缺少一个终止符(因此它被锁定并且不能被 mux 函数访问)。如果是这样,这是如何实现的?
编辑 经过更多测试后,该问题似乎仅在尝试使用多个管道重定向时发生。例如:
#!/bin/bash
pipe1="/tmp/pipe1"
mkfifo "${pipe1}"
trap "rm -rf ${pipe1}" EXIT
(exec >"${pipe1}"; echo "Test") &
cat < "${pipe1}"
将按预期工作。但是,添加 STDOUT(例如)会破坏它,迫使它挂起:
#!/bin/bash
pipe1="/tmp/pipe1"
mkfifo "${pipe1}"
trap "rm -rf ${pipe1}" EXIT
(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}"
cat < "${pipe2}"
更具体地说,一旦exec >"${pipe1}" 2>"${pipe2} 语句执行,代码就会挂起。我想在某些地方添加更多子shell会有所帮助,但这可能会变得混乱/笨拙。但是,我确实了解到,命名管道是为了在 shell 之间桥接数据(因此添加了子 shell 和后台运算符 &)。
【问题讨论】:
-
为了能够写入命名管道,必须同时打开管道进行读取。否则写入进程将阻塞,直到进程打开管道进行读取。管道的两端必须可用
-
@oguzismail 这很有道理。我找到了this site,它在文章的中途解释了这个原则。当然,没有两端的管道只会泄漏:)。但是,在向管道添加信息之前,我应该调用
cat命令(或等效的输入阅读器)吗?我不太确定如何在不孤立进程的情况下同时读写(这也不起作用)。 -
其实我今天想了一些你的问题,但没有想出解决方案,对不起:/
-
没关系。我很欣赏你的想法。我进行了更多测试并缩小了问题范围。我将在上面添加一个编辑。
标签: bash pipe stdout named-pipes indirection