如何在shell管道中使用不同的文件描述符?

4

我正在处理一个脚本,首先调用一个嘈杂的程序(在stdoutstderr上都有很多诊断信息),然后使用其他工具处理其输出。

该程序的冗长使得无法简单地将其stdout发送到管道中,因此目前我们使用一个临时文件——一种我想结束的做法。

我们可以要求该程序将数据写入/dev/fd/N,而不是/tmp/foo——这样做没有问题(例如,它不需要seek文件)。

当前发送到stdoutstderr的噪音可以继续存在——操作员已经习惯了它,如果它消失了,他们会感到惊慌……

但是,我该如何安排描述符N的存在并将其发送到下一个程序的stdin中?

noisy -o /dev/fd/N ?????| filter -i /dev/stdin

如果需要使用 bash,那就用吧,但我更希望有一个适用于整个sh家族的解决方案。


我认为您应该继续使用临时文件。 - oguz ismail
5
临时文件不好。它们效率低下,而且会在文件系统中留下垃圾 -- 编写可靠的清理代码比我的问题的任何答案都难看。可能有人会在_匆忙_中使用它们 -- 太忙了,无暇去做_正确_的事情。但是像你这样鼓励它们的使用,比使用它们本身更加错误。 - Mikhail T.
3
好的,祝你好运。 - oguz ismail
你能否使用命名管道,例如在Linux Bash中使用命名管道的示例 - Shane Bishop
@ShaneBishop,虽然命名管道没有临时文件的效率问题,但它们仍会在文件系统中留下垃圾。我非常感激pjh的答案-每个人都应该理解它以使他们的shell脚本更好。 - Mikhail T.
显示剩余3条评论
2个回答

7
如果我正确理解您的问题,您拥有一个程序,它将噪音写入标准输出和标准错误,并将有用的数据写入使用-o选项指定的文件。 您想保留标准输出和标准错误不变,但是将有用的数据导入过滤器程序,而不是写入文件。
使用Bash实现这个最简单的方法是使用进程替换(参见 进程替换- Greg's Wiki )。
noisy -o >(filter -i /dev/stdin)

请注意,在一些sh族壳并不支持进程替代,一些(罕见的)平台上Bash也不支持进程替代,而在Bash 4.4版本之前,没有办法获取使用进程替代创建的进程的退出状态。
另一种可能的方法是:
exec 3>&1
{ exec 4>&1; noisy -o /dev/fd/4 >&3 ; } | filter -i /dev/stdin
  • exec 3>&1 将文件描述符3指向"真正的"标准输出。
  • exec 4>&1 (因为它是在管道的第一阶段运行) 将文件描述符4指向管道中下一阶段的输入。
  • noisy ... >&3 强制noisy 的标准输出流去到"真正的"标准输出流。
  • /dev/fd/4写入(至少在Linux上)会写入管道中的下一阶段。

我只在Bash中测试过,但我认为它应该可以在其他sh系列shell中工作。


谢谢你的解决方案!老实说,我不明白如何让我的问题更清晰 - 但我很高兴你懂了 :) - Mikhail T.

0
这里有一种可能的方法:
( \
  /my/noisy/script \
    2> >(tee /tmp/stderr.log) \
    1> >(tee /tmp/stdout.log) \
) 2>&1 | tee /tmp/both.log

tee调用替换为您喜欢的任何过滤器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接