Bash 多进程替换重定向命令

Bash Multiple Process Substitution Redirect Order

我试图在 BASH 命令中使用多个进程替换,但我似乎误解了它们相互解析和重定向的顺序。

系统

Ubuntu 18.04
BASH 版本 - GNU bash,版本 4.4.20(1)-release (x86_64-pc-linux-gnu)

问题

我正在尝试将命令的输出重定向到 tee,将其重定向到 ts(添加时间戳),然后将其重定向到 split(拆分输出到单独的文件中)。我可以将输出重定向到 teets,但是当重定向到 split 时,我 运行 遇到了问题。

我的尝试

command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' > tempfile.txt)) - 这会将输出重定向到 tee 的进程替换,然后重定向到进程替换 ts 并添加时间戳,然后重定向到 tempfile.txt 这就是我所期望的

command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' >(split -d -b 10 -))) - 尽管我希望结果是一堆 10 字节的文件,在不同的行上带有时间戳,但它什么也没做。

为了继续测试,我尝试使用 echo 来查看会发生什么 command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' >(echo))) - 初始 tee 的打印(应该如此)但 echo 打印空行 显然这无关紧要,因为我得到了新结果 - 请参阅编辑在底部

command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]') >(split -d -b 10 -)) - 这将打印带有时间戳的命令(如 teets 应该的那样)并且另外创建带有命令输出的 10 字节文件(上面没有时间戳) . - 这是我所期望的并且是有意义的,因为 T 恤分别重定向到两个进程替换,这主要是一个完整性检查

我认为正在发生的事情

据我所知,>(ts '[%Y-%m-%d %H:%M:%S]' >(split -d -b 10 -)) (和 >(ts '[%Y-%m-%d %H:%M:%S]' >(echo)) 就此而言) 首先解析为一个完整且独立的命令。因此 split(和 echo)从 ts 接收到一个空输出,它本身没有输出。只有在这之后,实际的命令才会解析并将其输出发送到它的替换 tee.

这并不能解释为什么 command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' > tempfile.txt)) 确实有效,因为根据这个理论 tee 本身没有输出,所以 ts 应该接收不到输入并且也应该输出空白。

这就是说我不太确定发生了什么。 我认为这与解决流程替换的顺序以及重定向的发生方式有关,但对我来说它到底是如何发生的没有任何意义。

我想要的

基本上我只是想了解如何使 command >(tee -a >(ts '[%Y-%m-%d %H:%M:%S]' >(split -d -b 10 -))) 以它看起来应该的方式工作。我需要命令输出将其自身发送到进程替换 tee,进程替换 tee 将其发送到进程替换 ts 并添加将其发送到 split 的时间戳并将输出拆分为多个小文件。

感谢您抽出宝贵时间以及您可能提供的任何帮助。

*Edit - 我刚刚尝试 command > >(echo) 并看到输出是空白的,这不是我所期望的(我期望 echo 接收和然后输出命令输出)。我想我只是非常误解流程替换在这一点上的工作方式

如果你真的想让一个命令重定向 stdin/stderr 到一个单独的 ts|tee|split 你可以做的一件事是这个

command 1> >(ts '[%Y-%m-%d %H:%M:%S]' | tee -a >(split -d -b 10 -)) 2> >(ts '[%Y-%m-%d %H:%M:%S]' | tee -a >(split -d -b 10 -))

但缺点是 tee 仅在打印提示后打印。可能有一种方法可以通过复制文件描述符来避免这种情况,但这是我能想到的最好的方法。

如果需要,您可以将错误流从命令发送到与输出不同的管道中:

{ { cmd 2>&3 | ts ... | split; } 3>&1 >&4 | ts ... | split; } 4>&1

这会将 cmd 的输出发送到第一个管道,而来自 cmd 的错误流将进入第二个管道。引入文件描述符 3 是为了将来自 tssplit 的错误流分开,但这可能是不可取的。引入 fd 4 是为了防止 split 的输出被第二条管道消耗,这可能是不必要的(例如,如果 split 不产生任何输出。)

这个:

ts '[%Y-%m-%d %H:%M:%S]' >(split -d -b 10 -)

扩展了ts的命令行中进程替换生成的文件名,所以得到的运行是类似ts '[%Y-%m-%d %H:%M:%S]' /dev/fd/63的东西。 ts 然后尝试打开进入 splitfd 以从那里读取输入,而不是从原始标准输入读取。

这可能不是你想要的,在我的机器上,我在测试时得到了一些 tssplit 的副本卡在后台。可能相互连接成功,这可以解释缺少错误消息。

你可能打算写

ts '[%Y-%m-%d %H:%M:%S]' > >(split -d -b 10 -)
                         ^

重定向到进程替换。

也就是说,您可以在 tssplit 之间使用管道。