Bash将所有输出重定向到命名管道

时间:2018-09-05 02:19:24

标签: bash pipe stdout named-pipes indirection

我一直在寻找一种使用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之间桥接数据的(因此,添加了子shell和后台操作符&)。

1 个答案:

答案 0 :(得分:1)

如果要在关闭文件描述符后能够读取内容,则只需使用文件即可。使用管道的想法是,在写入命令之前,先要运行读取命令。

在这种设置下:

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}"

外壳将在此被阻止时阻塞:

(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}"
cat < "${pipe2}"

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

这里发生的是,即使打印机功能存在,管道也始终处于打开状态以进行写入,并且一直保持打开状态,直到运行进程替换为止。您可以通过exec 5>&-手动关闭它们,这会将EOF写入管道,从而使cat $pipe3正常返回。如果函数未关闭文件描述符,cat "$pipe1"将永远不会退出,这就是使用超时函数的原因,这样我们就可以在不阻塞管道的情况下清空管道。