是否有官方的 POSIX、GNU 或其他指南来说明应该在哪里打印进度报告和日志信息(例如“Doing foo; foo done”)?就我个人而言,我倾向于将它们写入 stderr,这样我就可以重定向 stdout 并仅获取程序的实际输出。最近有人告诉我,这不是一个好的做法,因为进度报告实际上并不是错误,只有错误消息应该打印到 stderr。
这两种立场都有道理,当然你可以根据你所做的事情的细节来选择其中一种,但我想知道这是否有一个普遍接受的标准。我无法在 POSIX、GNU 编码标准或任何其他此类广泛接受的最佳实践列表中找到任何具体规则。
我们有一些类似的问题,但它们没有解决这个确切的问题:
何时在 shell 脚本中使用重定向到 stderr:接受的答案表明我倾向于做什么,将程序的最终输出保留在标准输出上,并将其他任何内容保留在标准错误上。然而,这只是作为用户的意见提出的,尽管有论据支持。
使用消息应该发送到 stderr 还是 stdout?:这是特定于帮助消息的,但引用了 GNU 编码标准。这就是我正在寻找的东西,不仅仅是帮助消息。
那么,对于应该在哪里打印进度报告和其他信息性消息(不属于程序实际输出的一部分)是否有任何官方规则?
答案1
答案2
Posix 定义了标准流因此:
程序启动时,应预定义三个流,无需显式打开:标准输入(用于读取常规输入),标准输出(用于写入常规输出),以及标准误(用于写入诊断输出)。打开时,标准错误流未完全缓冲;当且仅当可以确定标准输入和标准输出流不引用交互设备时,标准输入和标准输出流才被完全缓冲。
这GNU C 库类似地描述标准流:
多变的:文件*标准输出
标准输出流,用于程序的正常输出。多变的:文件*标准错误
标准错误流,用于程序发出的错误消息和诊断。
因此,除了“常规/正常输出”和“诊断/错误输出”之外,标准定义对流的使用几乎没有指导。在实践中,通常将这些流中的一个或两个重定向到文件和管道,其中进度指示器将成为问题。有些系统甚至监视器 stderr
输出并将其视为问题的征兆。因此,纯粹的辅助进度信息在任一流上都是有问题的。
而不是无条件发送进度指示器任何一个标准流,重要的是要认识到进度输出仅适用于交互的溪流。考虑到这一点,我建议仅在检查流是否是交互式的之后才编写进度计数器(例如,isatty()
)或通过命令行选项显式启用时。这对于依赖终端更新行为才有意义的进度表尤其重要,例如完成百分比栏。
对于某些非常简单的进度消息(“开始 X”...“完成 X”),即使对于非交互式流,包含输出也更合理。在这种情况下,请考虑用户如何与流交互,例如使用 进行搜索grep
、分页less
或监控tail -f
。如果在这些上下文中查看进度消息有意义,那么它们将更容易从stdout
.
答案3
根据排除原则,只能去stderr。是的,我知道您询问了官方规范,除了 Stephen Kitt 给出的 POSIX 规范的链接之外,我无法向您提供该规范,该规范指出 stderr 用于诊断目的。
更重要的一点是 stdin 和 stdout 具有不允许将进度报告打印到 stdout 的功能 - 它们当然形成管道序列,这在 Unix shell 命令中不仅仅是副作用,而是强大的管道方法的核心。
所以。除了程序的真正“有效负载”之外,没有任何内容属于标准输出。如果您的程序没有输出,则不应将任何内容发送到标准输出。这使得 stderr 为其他一切,包括进度报告。
当然,这留下了一个漏洞——如果有一个“stdfluff”或类似的东西可能会很好,它既不用于输出也不用于错误,而是用于进度报告、调试等。事实上,没有什么可以阻止您这样做,即您可以将进度打印到文件描述符 3。示例:
$ perl -e 'open($fd, ">", "/dev/fd/3"); print $fd "hello\n"'
这不会产生任何输出。 (*)
$ perl -e 'open($fd, ">", "/dev/fd/3"); print $fd "hello\n"' 3>&1
hello
这会打印到 fd-3,然后重定向到 stdout。
(*) 第一个示例没有产生输出,但仍然有点牵强;失败open
并$!
包含no such file or directory
;就拿这个来说吧,认真这样用当然不行。在实际的程序中,如果你想走这条路,你可以测试一下是否/dev/fd/3
可用,并以此作为是否激活你的进度报告的提示;你必须很早就这样做,这样你就不会被你自己的open
真实文件之类的东西搞糊涂了......
答案4
如果用户调用了使用消息,--help
则使用消息应发送至 stdout;如果用户犯了错误,则应发送至 stderr。无条件地将其打印到 stderr 是令人讨厌的,因为它使得使用寻呼机/grep/等查看它变得更加困难。
另外,我可以建议第三种方法:打开 /dev/tty 来获取进度报告/spinners/等。