【发布时间】:2016-03-15 03:39:46
【问题描述】:
问题
因此,您希望(分别)记录一个进程或子进程的 stdout 和 stderr,如果您没有记录任何内容,则输出不会与您在终端中看到的不同。
看起来很简单,不是吗?不幸的是,似乎不可能为这个问题编写一个适用于任何给定进程的通用解决方案......
背景
管道重定向是分离标准输出和标准错误的一种方法,允许您单独记录它们。不幸的是,如果您将 stdout/err 更改为管道,则该进程可能会检测到该管道不是 tty(因为它没有宽度/高度、波特率等)并可能相应地改变其行为。为什么要改变行为?好吧,一些开发人员会使用终端的功能,如果您正在写入文件,这些功能就没有意义。例如,加载条通常需要将终端光标移回行首,并用新长度的条覆盖先前的加载条。颜色和字体粗细也可以在终端中显示,但在平面 ASCII 文件中则不能。如果您要将此类程序的标准输出直接写入文件,则该输出将包含所有终端 ANSI 转义码,而不是正确格式化的输出。因此,开发人员在向 stdout/err 写入任何内容之前实现了某种“isatty”检查,因此如果该检查返回 false,它可以为文件提供更简单的输出。
这里通常的解决方案是通过使用 pty 来诱使此类程序认为管道实际上是 tty - 一个双向管道,它也具有宽度、高度等。您将进程的所有输入/输出重定向到此 pty,并且这会诱使该过程认为它与真正的终端对话(您可以将其直接记录到文件中)。唯一的问题是,通过对 stdout 和 stderr 使用单个 pty,我们现在无法再区分两者。
因此,您可能希望为每个管道尝试不同的 pty - 一个用于标准输入,一个用于标准输出,一个用于标准错误。虽然这将在 50% 的时间内有效,但不幸的是,许多进程会进行额外的重定向检查,以确保 stdout 和 stderr (/dev/tty000x) 的输出路径相同。如果不是,则必须进行重定向,因此它们给您的行为与您在没有 pty 的情况下通过管道传输 stderr 和 stdout 相同。
您可能认为这种过度检查重定向并不常见,但不幸的是它实际上相当普遍,因为许多程序重用其他代码进行检查,例如 OSX 中的这段代码:
挑战
我认为找到解决方案的最佳方式是挑战。如果有人可以运行以下脚本(理想情况下是通过 Python,但此时我会采取任何措施)以分别记录 stdout 和 stderr 的方式,并且您设法欺骗它认为它是通过 tty 执行的,你解决了问题:)
#!/usr/bin/python
import os
import sys
if sys.stdout.isatty() and sys.stderr.isatty() and os.ttyname(sys.stdout.fileno()) == os.ttyname(sys.stderr.fileno()):
sys.stdout.write("This is a")
sys.stderr.write("real tty :)")
else:
sys.stdout.write("You cant fool me!")
sys.stdout.flush()
sys.stderr.flush()
请注意,解决方案应该真正适用于任何流程,而不仅仅是此代码。覆盖 sys/os 模块并使用 LD_PRELOAD 是克服挑战的非常有趣的方法,但它们并没有解决问题的核心:)
【问题讨论】:
-
您可能希望通过在 Q 中添加赏金来增加成功的机会。祝您好运!
-
好主意!我会在 2 天的冷却期到期后做 :)
-
我不会称这为“愚弄 isatty()”,而是愚弄 stty 的
checkredirect(),这要严格得多。 -
谢谢@CharlesDuffy,我已更新问题以更好地反映我正在寻找适用于任何给定流程的通用解决方案,而不仅仅是这段特定的代码。