因此,您需要记录一个进程或子进程的stdout和stderr(单独),而输出与您在没有记录任何内容的终端中看到的输出不同。在
看起来很简单不?不幸的是,它似乎不可能为这个问题写一个通用的解决方案,它适用于任何给定的过程。。。在
管道重定向是分离stdout和stderr的一种方法,允许分别记录它们。不幸的是,如果将stdout/err更改为管道,进程可能会检测到管道不是tty(因为它没有宽度/高度、波特率等),并可能相应地更改其行为。为什么要改变行为?好吧,有些开发人员利用终端的特性,如果你在写文件,这些特性就没有意义了。例如,加载条通常要求将终端光标移回行首,并用新长度的条覆盖上一个加载条。也可以在终端中显示颜色和字体粗细,但在一个简单的ASCII文件中则不能。如果要将这样一个程序的stdout直接写入一个文件,那么输出将包含所有终端ANSI转义码,而不是正确格式化的输出。因此,在向stdout/err写入任何内容之前,开发人员实现了某种“isatty”检查,因此如果检查返回false,它可以为文件提供更简单的输出。在
这里通常的解决方案是通过使用pty(一种双向管道,也有宽度、高度等)来欺骗这些程序,使其认为管道实际上是tty。您可以将进程的所有输入/输出重定向到此pty,这会使进程认为它在与一个真正的终端(并且可以直接将其记录到一个文件中)。唯一的问题是,通过对stdout和stderr使用一个pty,我们现在不能再区分这两者了。在
因此,您可能需要为每个管道尝试不同的pty—一个用于stdin,一个用于stdout,一个用于stderr。虽然这将在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斨u PRELOAD是一种非常有趣的战胜挑战的方法,但它们并不能解决问题的核心:)
像这样?在
因为我有点作弊。;—)
^{pr2}$像这样。。。在
我现在觉得脏兮兮的,但很有趣。:天
您总是可以分配伪TTY,这就是
screen
所做的。在在Python中,可以使用
pty.openpty()
来访问它此“主”代码通过测试:
当然,如果您想区分stdin/out/err,“slave”进程将看到不同的PYT名称:
^{pr2}$对于更简单的用例(例如开发测试),使用
strace
(linux)或dtruss
(OSX)。当然,这在特权进程中是行不通的。在这是一个示例,您可以区分}fd2:
stdout
fd1和{在上面的示例中,您可以看到每个
standard xxx data
加倍,因为您不能重定向stdout/stderr。但是,您可以要求strace
将其输出保存到文件中。在从理论上讲,如果}指的是同一个终端,那么在进程上下文中,无论是在用户模式(LD_PRELOAD),还是在内核空间(strace工具使用的ptrace接口)中,您只能区分这两个终端。一旦数据击中实际设备,真实的或伪的,区别就失去了。在
stdout
和{相关问题 更多 >
编程相关推荐