简体   繁体   English

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

[英]Bash redirect all output to named pipes

I've been looking for a way to use Bash indirection to re-route all outputs (1 (STDOUT), 2 (STDERR), 3, etc.) to named pipes. 我一直在寻找一种使用Bash间接将所有输出(1(STDOUT),2(STDERR),3等)重新路由到命名管道的方法。 Here is a script that I wrote to test this theory: 这是我为测试该理论而编写的脚本:

#!/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

This code seems to be alright, but the terminal hangs indefinitely until it's terminated. 这段代码似乎还不错,但是终端会无限期地挂起,直到终止为止。 As I understand it, pipes are like files in that they have an inode, but rather than writing to disk, they simply write to memory. 据我了解,管道就像文件一样,因为它们具有一个索引节点,但是它们不是写入磁盘,而是直接写入内存。

That being said, it should be accessible like any other file. 话虽如此,它应该像任何其他文件一样可访问。 I know the script hangs on the line calling the printer function. 我知道脚本挂在调用打印机函数的行上。 I have also tested several combinations of subshells and more advanced redirections (namely, redirecting to STDOUT to handle each of the other pipes). 我还测试了子外壳程序和更高级的重定向(即,重定向到STDOUT以处理其他每个管道)的几种组合。 Perhaps I am missing a terminator in the named pipe (Whereby it is locked and cannot be accessed by the mux function). 也许我在命名管道中缺少终止符(因此该终止符已被锁定,无法通过mux函数访问)。 If that is the case, how is this achieved? 如果是这样,这是如何实现的?

EDIT After more testing, it appears that the issue only happen when attempting to redirect with multiple pipes. 编辑经过更多测试后,看来该问题仅在尝试使用多个管道进行重定向时发生。 For example: 例如:

#!/bin/bash

pipe1="/tmp/pipe1"
mkfifo "${pipe1}"
trap "rm -rf ${pipe1}" EXIT

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

will work as expected. 将按预期工作。 However, adding STDOUT (for example), will break this, forcing it to hang: 但是,添加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}"

More specifically, the code hangs once the exec >"${pipe1}" 2>"${pipe2} statement executes. I imagine that adding more subshells in certain places will help, but this may become messy/unwieldy. I did learn, however, that the named pipes are meant to bridge data between shells (hence the added subshells and background operator & ). 更具体地说,一旦exec >"${pipe1}" 2>"${pipe2}语句,代码就会挂起。我想在某些地方添加更多的子shell会有所帮助,但这可能会变得凌乱/笨拙。但是,命名管道是用来在外壳之间桥接数据的(因此添加了子外壳和背景运算符& )。

If you want to be able to read the content after the file descriptor was closed, you need to just use files. 如果要在关闭文件描述符后能够读取内容,则只需使用文件即可。 The think with pipes is, that the reading command needs to be running first before the command that writes. 使用管道的想法是,在写入命令之前,先要运行读取命令。

In such setup: 在这样的设置中:

cmd1 | cmd2 | cmd3

cmd3 is run first, then cmd2 , then cmd1 . 首先运行cmd3 ,然后运行cmd2 ,然后运行cmd1 So if you want to setup it using pipes, you would need to open each fifo for reading in parallel and then call printer : 因此,如果要使用管道进行设置,则需要打开每个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}"

The shell will block on this snipped: 外壳将在此被阻止时阻塞:

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

On cat < "$pipe1" Cause you need to read from both pipes for the exec to continue: cat < "$pipe1"因为您需要从两个管道中读取内容才能使exec继续:

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

If you want buffered output from a command, ie. 如果要从命令缓冲输出,即。 read the output of the command after it had written something or exited, use just files for that, they are actually called logs. 写入命令或退出命令后,读取命令的输出,仅使用该命令的文件,它们实际上称为日志。

As a workaround, you can use bash pipe internal buffering to buffer your messages: 解决方法是,可以使用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

What happens here, is that pipes are always open for writing, even after the printer function exists and will remain open until the process substitution is running. 这里发生的是,即使打印机功能存在,管道也始终处于打开状态以进行写入,并且一直保持打开状态,直到运行进程替换为止。 You can close them manually, by exec 5>&- , which will write EOF to the pipe letting cat $pipe3 return normally. 您可以通过exec 5>&-手动关闭它们,这会将EOF写入管道,从而使cat $pipe3正常返回。 cat "$pipe1" will never exit if the function does not close the file descriptors, that's why the timeout functions are used, so that we can drain pipes without blocking on them. 如果函数未关闭文件描述符, cat "$pipe1"将永远不会退出,这就是使用超时函数的原因,这样我们就可以在不阻塞管道的情况下将其cat "$pipe1"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM