进度报告/日志信息是否属于 stderr 或 stdout?

ter*_*don 96 standard gnu posix stdout stderr

是否有官方的 POSIX、GNU 或其他指南来说明应该在何处打印进度报告和日志信息(例如“Doing foo; foo done”)?就个人而言,我倾向于将它们写入 stderr,这样我就可以重定向 stdout 并仅获取程序的实际输出。我最近被告知这不是一个好的做法,因为进度报告实际上并不是错误,只有错误消息应该打印到 stderr。

这两个职位都有意义,当然您可以根据您正在做的事情的细节选择一个或另一个,但我想知道是否有一个普遍接受的标准。我无法在 POSIX、GNU 编码标准或任何其他此类广泛接受的最佳实践列表中找到任何特定规则。

我们有一些类似的问题,但它们没有解决这个确切的问题:

那么,对于进度报告和其他信息性消息(不是程序实际输出的一部分)应该在哪里打印,是否有任何官方规则?

Ste*_*itt 81

POSIX将标准错误定义为

用于写入诊断输出

这并不仅限于错误消息。我会将进度信息视为诊断输出,因此它属于标准错误。

  • @PM2Ring 并非特定于 Python,POSIX 以这种方式定义了流。 (15认同)
  • FWIW,在 Python 中,`stdout` 默认是行缓冲的,而 `stderr` 是无缓冲的,所以 `stderr` 是编写不包含换行符的进度文本/条/微调器的自然选择。(如果你在 `stdout` 上写了这样的文本,你需要用 `stdout.flush()` 把你的进度输出调用弄乱以使其可见)。 (9认同)
  • @PM2Ring:写入`stdout` 时,如果您希望进度报告在重定向时实时显示,即使您的文本*确实* 包含换行符,也需要刷新。 (6认同)

小智 38

POSIX定义标准流从而

在程序启动时,应预定义三个流,无需明确打开:标准输入(用于读取常规输入)、标准输出(用于写入常规输出)和标准错误(用于写入诊断输出)。打开时,标准错误流没有完全缓冲;当且仅当可以确定流不是指交互设备时,标准输入和标准输出流才被完全缓冲。

GNU C库描述了同样的标准流:

变量:FILE * stdout
标准输出流,用于程序的正常输出。

变量:FILE * stderr
标准错误流,用于程序发出的错误信息和诊断信息。

因此,除了“常规/正常输出”和“诊断/错误输出”之外,标准定义对流使用几乎没有指导。在实践中,将这些流中的一个或两个重定向到文件和管道是很常见的,在那里进度指示器将是一个问题。一些系统甚至监视 stderr输出并将其视为问题的迹象。因此,纯辅助进度信息在任一流上都是有问题的。

与其无条件地向任一标准流发送进度指示器,重要的是要认识到进度输出仅适用于交互式流。考虑到这一点,我建议仅在检查流是否交互(例如,使用isatty())或通过命令行选项显式启用后才编写进度计数器。这对于依赖终端更新行为有意义的进度表尤其重要,例如 %-complete 条。

对于某些非常简单的进度消息(“Starting X”...“Done with X”),即使对于非交互式流也包含输出更为合理。在这种情况下,请考虑用户如何与流交互,例如使用 进行搜索、grep分页less或监视tail -f。如果在这些上下文中查看进度消息有意义,那么从stdout.


mur*_*uru 14

POSIX 对Shell and Utilities 1.4: Utility Description Defaults(强调我的)中的“诊断信息”稍微具体一点:

STDERR

STDERR 部分描述了实用程序的标准错误输出。仅描述了实用程序有意发送的那些消息。将终端用于标准错误可能会导致任何写入标准错误输出的标准实用程序在后台使用时停止。出于这个原因,应用程序不应使用脚本中的交互功能放置在后台。

大多数实用程序的诊断消息格式未指定,但本卷 POSIX.1-2008 未指定格式的诊断和信息性消息的语言和文化约定应受 LC_MESSAGES 和 [XSI] [选项启动] 设置的影响] NLSPATH。[选项结束]

标准实用程序的指定标准错误输出不应依赖于本 POSIX.1-2008 卷中定义的环境变量的存在或值,除非本卷 POSIX.1-2008 提供。

默认行为:当此部分被列为“标准错误应仅用于诊断消息。”时,这意味着除非另有说明,否则仅当退出状态指示错误时才将诊断消息发送到标准错误发生并且该实用程序的使用方法如本卷 POSIX.1-2008 所述。

当本节被列为“未使用”时,表示按照POSIX.1-2008本卷中所述使用该实用程序时不得使用标准错误。

IANASL,但我认为这意味着只有当实用程序返回错误退出代码时,stderr 才会有输出。由于这不应该是成功执行的正常事件过程,除非发生错误(当然,除非另有说明等),否则POSIX 实用程序不应打印任何进度信息

  • 好吧,我想说“除非另有说明”是一个相当重要的警告:POSIX 实用程序可以很好地指定它输出有关其标准错误的进度信息,并且它符合标准。您说得对,有趣的是“默认行为”(仍然需要在相关部分中具体提及)仅在退出代码也指示错误时才使用标准错误;但这不是限制。 (3认同)
  • @StephenKitt 我也没有。但他正在与他的 CTO 辩论,他说 stderr 输出表明出现问题,并且标准确实支持他作为默认行为的主张。 (2认同)
  • 它仅支持将声明作为默认行为*对于 POSIX 实用程序*,甚至仅在特定情况下(使用提到的短语的实用程序)。POSIX 的这部分不是规范性的 (AIUI),它是一个模板:它告诉您如何阅读实用程序规范,它没有指定实用程序的行为。具体来说,它定义了短语“标准错误应仅用于诊断消息”的含义。和“未使用”。在实用程序规范的“STDERR”部分。每个实用程序都指定了它的行为,并且它可以使用这些短语作为速记。 (2认同)

Ano*_*noE 13

按照排除原则,只能去stderr。是的,我知道您询问了官方规范,除了 Stephen Kitt 给出的 POSIX 规范的链接之外,我无法向您介绍该规范,该规范指出 stderr 用于诊断目的。

更重要的一点是 stdin 和 stdout 有一个函数,它不允许向 stdout 打印进度报告——它们当然形成了管道序列,在 Unix shell 命令中这不仅是一个副作用,而且是强大的管道方法的核心.

所以。除了程序的真正“有效负载”之外,没有任何东西属于标准输出。如果您的程序没有输出,那么什么都不应该进入标准输出。这将 stderr 留给其他所有内容,包括进度报告。

当然,这留下了一个漏洞——有一个“stdfluff”或类似的东西可能会很好,它既不是输出也不是错误,而是进度报告、调试等等。事实上,没有什么可以阻止您这样做,即,您可以将进度打印到文件描述符 3。示例:

$ perl -e 'open($fd, ">", "/dev/fd/3"); print $fd "hello\n"'
Run Code Online (Sandbox Code Playgroud)

这不会产生任何输出。(*)

$ perl -e 'open($fd, ">", "/dev/fd/3"); print $fd "hello\n"'  3>&1
hello
Run Code Online (Sandbox Code Playgroud)

这会打印到 fd-3,然后重定向到 stdout。

(*) 第一个例子没有输出,但仍然有点牵强;的open失败,$!将包含no such file or directory; 就以这个为例,当然不能这么认真地使用。在实际程序中,如果你想走这条路,你可以测试是否/dev/fd/3可用,以此作为是否激活你的进度报告的提示;你必须尽早这样做,这样你就不会被你自己open的真实文件和诸如此类的s弄糊涂了。