Bash 将所有输出重定向到命名管道
Bash redirect all output to named pipes
我一直在寻找一种方法来使用 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
这段代码似乎没问题,但终端会无限期挂起,直到它终止。据我了解,管道就像文件一样,因为它们有一个索引节点,但它们不是写入磁盘,而是简单地写入内存。
也就是说,它应该像任何其他文件一样可以访问。我知道脚本挂在调用打印机函数的行上。我还测试了几种子外壳和更高级重定向的组合(即重定向到 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 会有所帮助,但这可能会变成 messy/unwieldy。然而,我确实了解到命名管道是为了在 shell 之间桥接数据(因此添加了子 shell 和后台运算符 &
)。
如果你想在文件描述符关闭后能够读取内容,你只需要使用文件。管道的想法是,读取命令需要先 运行ning 在写入命令之前。
在这样的设置中:
cmd1 | cmd2 | cmd3
cmd3
先是运行,然后是cmd2
,然后是cmd1
。所以如果你想使用管道设置它,你需要打开每个 fifo 并行读取然后调用 printer
:
printer() {
echo "OUT" >&1
echo "ERR" >&2
echo "WRN" >&3
}
# Usage: mux
mux() {
cat "${pipe1}" &
cat "${pipe2}" &
cat "${pipe3}"
}
mux &
printer 1>${pipe1} 2>"${pipe2}" 3>"${pipe3}"
shell 将阻止此片段:
(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}"
cat < "${pipe2}"
On cat < "$pipe1"
因为您需要从两个管道读取才能让 exec 继续:
(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}" &
cat < "${pipe2}"
如果你想要一个命令的缓冲输出,即。在命令写入或退出后读取命令的输出,为此仅使用文件,它们实际上称为日志。
作为解决方法,您可以使用 bash 管道内部缓冲来缓冲您的消息:
printer() {
echo "OUT" >&3
echo "ERR" >&4
echo "WRN" >&5
}
# Usage: mux
mux() {
timeout 1 cat "${pipe1}"
timeout 1 cat "${pipe2}"
timeout 1 cat "${pipe3}"
}
printer 3> >(cat >$pipe1) 4> >(cat >$pipe2) 5> >(cat >$pipe3)
mux
这里发生的事情是,管道始终打开以进行写入,即使在打印机功能存在之后也将保持打开状态,直到进程替换 运行ning。您可以通过 exec 5>&-
手动关闭它们,这将正常将 EOF 写入让 cat $pipe3
return 的管道。 cat "$pipe1"
如果函数不关闭文件描述符,将永远不会退出,这就是使用超时函数的原因,这样我们就可以在不阻塞管道的情况下排出管道。
我一直在寻找一种方法来使用 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
这段代码似乎没问题,但终端会无限期挂起,直到它终止。据我了解,管道就像文件一样,因为它们有一个索引节点,但它们不是写入磁盘,而是简单地写入内存。
也就是说,它应该像任何其他文件一样可以访问。我知道脚本挂在调用打印机函数的行上。我还测试了几种子外壳和更高级重定向的组合(即重定向到 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 会有所帮助,但这可能会变成 messy/unwieldy。然而,我确实了解到命名管道是为了在 shell 之间桥接数据(因此添加了子 shell 和后台运算符 &
)。
如果你想在文件描述符关闭后能够读取内容,你只需要使用文件。管道的想法是,读取命令需要先 运行ning 在写入命令之前。
在这样的设置中:
cmd1 | cmd2 | cmd3
cmd3
先是运行,然后是cmd2
,然后是cmd1
。所以如果你想使用管道设置它,你需要打开每个 fifo 并行读取然后调用 printer
:
printer() {
echo "OUT" >&1
echo "ERR" >&2
echo "WRN" >&3
}
# Usage: mux
mux() {
cat "${pipe1}" &
cat "${pipe2}" &
cat "${pipe3}"
}
mux &
printer 1>${pipe1} 2>"${pipe2}" 3>"${pipe3}"
shell 将阻止此片段:
(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}"
cat < "${pipe2}"
On cat < "$pipe1"
因为您需要从两个管道读取才能让 exec 继续:
(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}" &
cat < "${pipe2}"
如果你想要一个命令的缓冲输出,即。在命令写入或退出后读取命令的输出,为此仅使用文件,它们实际上称为日志。
作为解决方法,您可以使用 bash 管道内部缓冲来缓冲您的消息:
printer() {
echo "OUT" >&3
echo "ERR" >&4
echo "WRN" >&5
}
# Usage: mux
mux() {
timeout 1 cat "${pipe1}"
timeout 1 cat "${pipe2}"
timeout 1 cat "${pipe3}"
}
printer 3> >(cat >$pipe1) 4> >(cat >$pipe2) 5> >(cat >$pipe3)
mux
这里发生的事情是,管道始终打开以进行写入,即使在打印机功能存在之后也将保持打开状态,直到进程替换 运行ning。您可以通过 exec 5>&-
手动关闭它们,这将正常将 EOF 写入让 cat $pipe3
return 的管道。 cat "$pipe1"
如果函数不关闭文件描述符,将永远不会退出,这就是使用超时函数的原因,这样我们就可以在不阻塞管道的情况下排出管道。