【问题标题】:Send stdout + stderr to file, AND send stderr + arbitrary status messages to console将 stdout + stderr 发送到文件,并将 stderr + 任意状态消息发送到控制台
【发布时间】:2019-01-21 12:51:36
【问题描述】:

我在 SO 中挖掘了大约 20 个问题,以寻找解决此问题的方法,但没有运气。这似乎是一项常见的任务。

我对 bash 脚本有以下要求:

  1. stdout 发送到log.out,而不是发送到控制台
  2. stderr 发送到log.out AND 到控制台
  3. 向控制台(和log.out?)发送任意状态消息

目前我正在尝试以下方法来实现这一目标:

#!/bin/bash

> log.out

exec 3>&1 4>&2
trap 'exec 2>&4 1>&3' 0 1 2 3
exec 1>>log.out 2>&1

# Test
echo "status goes to console" >&3
echo "stderr goes to file + console" >&2
echo "stdout goes to file"

我对这段代码的理解大致是……

  1. 将 stdout 备份到 FD 3,将 stderr 备份到 FD 4
  2. 脚本退出时重置它们(可能没有必要?)
  3. 将 stdout 发送到 log.out,并将 stderr 也发送到那里

除了错误不会显示到控制台之外,这一切正常。

所以,我想,我将在单独的 bg 进程中将 /dev/stderr 转换为 &3,并在第二个 exec 下添加这一行:

cat /dev/stderr >&3 &

我不明白为什么,但是,这也会将标准输出发送到 &3,所以我的控制台显示:

echoes goes to console
stderr goes to file + console
stdout goes to file

我已经尝试了大约 50 种组合,但均未成功。经过大量阅读后,我倾向于需要一个(自定义?)C 程序或类似程序来实现这一点,这对我来说似乎有点疯狂。

非常感谢任何帮助。


谢谢!

嘿,感谢@charles-duffy 的 cmets 和答案,我能够对现有脚本进行非常小的修改,从而实现了我正在寻找的总体思路:

exec 3>&1 4>&2
trap 'exec 2>&4 1>&3' 0 1 2 3
exec 1>log.out 2> >(tee /dev/tty >&1)

缺点是消息在日志文件中确实出现乱序。

【问题讨论】:

  • 您将失去写入之间的同步。
  • 就像写入控制台一样?只要log.out 文件的顺序正确,异步就可以了。
  • 事情就是这样——当你这样做时,你就失去了保证秩序的能力。我们有一些很好的副本,并深入解释了原因;寻找他们。
  • ...基本上,操作系统只保证写入两个 FD 的顺序将导致当它们都是 fdup()s 时追加按该顺序。如果两个 FD 指向不同的目的地,则它们不能相互重复。
  • 顺便说一句,> log.out 然后exec >>log.out 而不是单独的exec >log.out 有什么意义?无论哪种方式,您都在截断文件,然后在整个过程中附加到它。

标签: bash shell stdout stderr io-redirection


【解决方案1】:

假设您拥有 bash 4.1 或更高版本:

# make copies of orig stdout, /dev/tty, and our log file FDs
exec {orig_stdout_fd}>&1
exec {tty_fd}>/dev/tty
exec {file_fd}>log.out

# ...and set them up however you wish, using ''tee'' for anything that goes two places
exec >&$file_fd                                  # redirect stdout only to the log file
exec 2> >(tee /dev/fd/"$tty_fd" >&$file_fd)      # redirect stderr to both sinks

请注意,写入 stderr 将比写入 stdout 花费更长的时间(因为它们正在通过 FIFO),因此可能会丢失排序。

【讨论】:

  • 您好,非常感谢您的回答。到目前为止,我没有在 tty 中看到任意状态消息。目标是让(大型)配置脚本干净地将所有apt-get 垃圾发送到log.out,同时将漂亮的高级状态消息打印到控制台(例如安装 xyz...)并向控制台显示任何错误用户立即。理想情况下,日志包含所有内容(以防万一)。
  • stderr 应该用于all“诊断”内容(包括状态更新的类别),而不仅仅是错误。因此,将 stderr 定向给用户意味着您正在显示您所说的所有状态,而不仅仅是错误消息本身。
  • 无论如何,程序使用isatty() 来更改其默认日志级别是很常见的。我建议仔细看看这是否正在发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-29
  • 1970-01-01
  • 2015-07-04
  • 2011-10-19
  • 2021-11-09
相关资源
最近更新 更多