【问题标题】:Separately redirecting and recombining stderr/stdout without losing ordering分别重定向和重新组合 stderr/stdout 而不会丢失排序
【发布时间】:2018-01-27 09:05:37
【问题描述】:

我想执行一个命令,想重定向stderr和stdout如下:

stderr 和 stdout -> 应该只写入 logs.log 文件,同时保持顺序

stderr -> 应该打印到 SCREEN 并写入 errors.log

到目前为止,我可以将它们重定向到屏幕和文件 log.txt,如下所示:

command 2>&1 | tee logs.log

但以上不是我需要的。

为了更清楚地再次说明结果需要是什么。

执行命令后,我只需要在屏幕上看到 stderr 的结果,我需要一个名为 errors.log 的文件和 stderr,我需要另一个名为 logs.log 的文件,其中包含 stdout 和stderr 按照它们创建的原始顺序。

【问题讨论】:

  • 在保持秩序的同时进行单独的重定向实际上是不可能的:操作系统不提供支持它所需的保证。
  • 好吧,除非您依靠检测系统调用(例如从 sysdig 或 strace 重建输出),否则这是不可能的。
  • 顺便问一下,您正在运行的命令是什么?如果它是用带有日志框架的语言编写的,您可能能够获得所需的保证,您可以将其配置为执行所有必要的写入作为阻塞操作。
  • 顺便说一句,您指的是logs.logerrors.loglog.txt -- 除了控制台之外,您真的有 三个 预期的输出文件吗?
  • @CharlesDuffy 我编辑了我只有 2 个,log.txt 和 logs.log 是一样的。现在正在编辑问题

标签: linux bash scripting


【解决方案1】:

在执行单独的重定向的同时保持完美的顺序,如果没有一些丑陋的骇客,理论上是不可能的。 顺序只保留在直接写入同一文件(在 O_APPEND 模式下);只要您将tee 之类的内容放在一个进程中而不是另一个进程中,排序保证就会消失,并且如果不保留有关以何种顺序调用哪些系统调用的信息,就无法检索。

那么,那个黑客会是什么样子?它可能看起来像这样:

# eat our initialization time *before* we start the background process
sudo sysdig-probe-loader

# now, start monitoring syscalls made by children of this shell that write to fd 1 or 2
# ...funnel content into our logs.log file
sudo sysdig -s 32768 -b -p '%evt.buffer' \
  "proc.apid=$$ and evt.type=write and (fd.num=1 or fd.num=2)" \
  > >(base64 -i -d >logs.log) \
  & sysdig_pid=$!

# Run your-program, with stderr going both to console and to errors.log
./your-program >/dev/null 2> >(tee errors.log)

也就是说,这仍然是丑陋的骇客:它只捕获直接对 FD 1 和 2 的写入,而不会跟踪可能发生的任何进一步的重定向。 (这可以通过执行对 FIFO 的写入并使用 sysdig 跟踪对这些 FIFO 的写入来改进;这样fdup() 和类似的操作将按预期工作;但以上足以证明这个概念。


单独处理显式

在这里,我们演示了如何使用它来仅对 stderr 进行着色,而无需理会 stdout —— 通过告诉 sysdig 生成 JSON 流作为输出,然后对其进行迭代:

exec {colorizer_fd}> >(
  jq --unbuffered --arg startColor "$(tput setaf 1)" --arg endColor "$(tput sgr0)" -r '
    if .["fd.filename"] == "stdout" then
      ("STDOUT: " + .["evt.buffer"])
    else
      ("STDERR: " + $startColor + .["evt.buffer"] + $endColor)
    end
  '
)

sudo sysdig -s 32768 -j -p '%fd.filename %evt.buffer' \
  "proc.apid=$$ and evt.type=write and proc.name != jq and (fd.num=1 or fd.num=2)" \
  >&$colorizer_fd \
  & sysdig_pid=$!

# Run your-program, with stdout and stderr going to two separately-named destinations
./your-program >stdout 2>stderr

因为我们要关闭输出文件名(stdoutstderr),所以这些文件名需要保持不变,以上代码才能正常工作——任何需要的临时目录都可以使用。


显然,您实际上不应该这样做。更新您的程序以支持任何可用其本地语言(Java 中的 Log4j、Python 日志记录模块等)的日志记录基础设施,以允许它的日志记录要明确配置。

【讨论】:

  • 是的,这有很多开销,但感谢您的建议。该程序不是我的我只想记录以下复杂且不常见的方式。只是好奇有什么可能的解决方案来实现这一目标
【解决方案2】:

这将使您大部分时间到达那里:

your_command 2> >(tee -a logs.log errors.log) 1>>logs.log

但我认为您不能完全保留 logs.log 文件中的输出顺序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-22
    • 1970-01-01
    • 2012-09-25
    • 2021-10-18
    • 1970-01-01
    • 2013-01-23
    • 1970-01-01
    • 2012-08-07
    相关资源
    最近更新 更多