【问题标题】:Redirect both stdout and stderr to file, print stdout only [duplicate]将stdout和stderr都重定向到文件,仅打印stdout [重复]
【发布时间】:2018-09-05 23:59:28
【问题描述】:

我在stdoutstderr 中有大量文本;我想将所有内容记录在一个文件中(以相同的顺序),并在控制台中仅打印来自stdout 的内容以供进一步处理(如grep)。

> file&> file 的任何组合,即使与 ||& 将永久重定向流,之后我无法管道它:

my_command > output.log | grep something  # logs only stdout, prints only stderr
my_command &> output.log | grep something  # logs everything in correct order, prints nothing
my_command > output.log |& grep something  # logs everything in correct order, prints nothing
my_command &> output.log |& grep something  # logs everything in correct order, prints nothing

tee 的任何使用都会

  • 打印来自stderr的内容然后记录来自stdout的所有内容并打印出来,这样我就失去了输入文本的顺序
  • 如果我使用 |& tee,则以正确的顺序记录两者,但我无法控制流,因为现在所有内容都在 stdout 中。

示例:

my_command | tee output.log | grep something  # logs only stdout, prints all of stderr then all of stdout
my_command |& tee output.log | grep something  # logs everything, prints everything to stdout
my_command | tee output.log 3>&1 1>&2 2>&3 | tee -a output.log | grep something  # logs only stdout, prints all of stderr then all of stdout

现在我完全没有想法了。

这是我的测试用例的样子:

testFunction() { 
  echo "output"; 
  1>&2 echo "error"; 
  echo "output-2"; 
  1>&2 echo "error-2"; 
  echo "output-3"; 
  1>&2 echo "error-3";
}

我希望我的控制台输出看起来像:

output
output-2
output-3

我的 output.log 文件看起来像:

output
error
output-2
error-2
output-3
error-3

有关更多详细信息,我正在使用 grep 过滤 mvn clean install 的输出,以仅在终端中保留最少的信息,但我也希望在某处拥有完整的日志,以防我需要调查堆栈跟踪或者其他的东西。 java 测试日志被发送到stderr,所以我选择在控制台输出中丢弃它。

【问题讨论】:

  • 操作系统级缓冲意味着实际上没有办法在不同的文件句柄上建立特定的顺序。也许运行一个包装器,它在一个紧密的循环中读取两个无缓冲的流。
  • 可以重建原始顺序,但您需要类似sysdig(在执行前将系统调用转储到环形缓冲区)来保证正确性。
  • (我什至不会将其描述为“操作系统级缓冲”,这就是问题所在——即使您根本没有缓冲,tee 也需要时间来执行直接写入文件没有,让一个 FD 写入 FIFO 到 tee 和另一个直接写入文件不同步;同样,程序的调度可以随意延迟,因此不能保证两次写入 FIFO 连接到tee 的不同副本将导致这些副本被安排以与原始写入发生的顺序相同的顺序运行)。
  • related question 指定重新组合 stdoutstderr,但我的问题更多是关于过滤它们。是否没有 tee-equivalent 但它从两个流中读取并写入两个流?然后我可以简单地过滤它。
  • 分割成两个流的事实意味着不再有操作系统级别的接口(无需进入 MacOS/BSD 上的ptracesysdigdtrace 等)让您精确确定对两个不同流的一对写入的顺序。无论您是在过滤还是合并,都不是重点——重点是,如果您想区别对待流,但要保留写入之间的顺序对于他们,您需要使用类似于链接问题答案中给出的方法。

标签: bash maven io-redirection


【解决方案1】:

虽然不是真正使用重定向或任何该顺序的解决方案,但您可能希望为此使用annotate-output

假设script.sh包含你的函数,那么你可以这样做:

$ annotate-output ./script.sh                                                     
13:17:15 I: Started ./script.sh
13:17:15 O: output
13:17:15 E: error
13:17:15 E: error-2
13:17:15 O: output-2
13:17:15 E: error-3
13:17:15 O: output-3
13:17:15 I: Finished with exitcode 0

所以现在很容易重新处理该信息并将其发送到您想要的文件:

$ annotate-output ./script.sh \
  | awk '{s=substr($0,13)}/ [OE]: /{print s> "logfile"}/ O: /{print s}'
output
output-2
output-3
$ cat logfile 
output
error
error-2
output-2
error-3
output-3

或者teesedcut的任何其他组合...

根据@CharlesDuffy 的评论:

由于 stdout 和 stderr 是并行处理的,因此可能会发生在 stdout 将显示在稍后打印的 stderr 行之前(反之亦然)。

不幸的是,目前的注释策略很难解决这个问题。修复将 涉及切换到 PTRACE'ing 过程。给予 nice (远)更高的优先级 但是,执行的程序可能会导致此行为的出现频率降低。

来源:man annotate-output

【讨论】:

  • 除非这被实现为像ptrace() 那样重定向系统调用,使用LD_PRELOAD 覆盖来覆盖C 标准库写入操作,或者以其他方式做monkey-in-the-middlehackery,它不能保证它保留原始顺序。
  • ...确实,链接手册页的BUGS 部分正是如此。
  • @CharlesDuffy 刚刚添加了手册页部分
  • @CharlesDuffy,也许将纳秒添加到时间 (+%s.%N),可能会起作用,然后使用排序。
  • 试图在一对单独/不同的 FIFO 的另一侧增加精度是试图从以太中创建信息——有时两次读取会以与成对写入相反的顺序发生,并记录更精确的潜在反转顺序无助于解决根本问题。
猜你喜欢
  • 2014-07-22
  • 2011-11-23
  • 1970-01-01
  • 2012-08-07
  • 2011-12-28
  • 2022-01-18
  • 1970-01-01
  • 2016-02-19
  • 1970-01-01
相关资源
最近更新 更多