当前位置: 首页 > 知识库问答 >
问题:

你能愚弄isatty并分别记录stdout和stderr吗?

戚侯林
2023-03-14

因此,您希望(分别)记录进程或子进程的stdout和stderr,而不会使输出与您在终端中看到的不同,如果您没有记录任何内容。

看起来很简单不是吗?不幸的是,似乎不可能为这个问题编写一个通用的解决方案,在任何给定的过程中都有效。。。

管道重定向是一种分离stdout和stderr的方法,允许您单独记录它们。不幸的是,如果将标准输出/错误更改为管道,则进程可能会检测到管道不是tty(因为它没有宽度/高度、波特率等),并可能相应地更改其行为。为什么要改变行为?嗯,一些开发人员利用终端的功能,如果您正在向文件写入数据,这些功能就没有意义。例如,加载条通常需要将终端光标移回行的开头,并用新长度的条覆盖以前的加载条。此外,颜色和字体重量可以在终端中显示,但在平面ASCII文件中不能显示。如果要将此类程序的标准输出直接写入文件,则该输出将包含所有终端ANSI转义码,而不是正确格式化的输出。因此,开发人员在将任何内容写入标准输出/错误之前执行某种“isatty”检查,因此如果该检查返回false,则可以为文件提供更简单的输出。

这里通常的解决方案是使用pty来诱使此类程序认为管道实际上是ttys——一种也有宽度、高度等的双向管道。您将进程的所有输入/输出重定向到这个pty,这会诱使进程认为它与真实终端对话(您可以将其直接记录到文件中)。唯一的问题是,通过为stdout和stderr使用单个pty,我们现在无法区分两者。

因此,您可能希望为每个管道尝试不同的pty——一个用于stdin,一个用于stdout,一个用于stderr。虽然这将在50%的时间内工作,但不幸的是,许多进程会进行额外的重定向检查,以确保stdout和stderr(/dev/tty000x)的输出路径相同。如果不是,则必须进行重定向,因此它们会给您提供与在没有pty的情况下通过管道传输stderr和stdout相同的行为。

您可能会认为这种对重定向的过度检查并不常见,但不幸的是,它实际上相当普遍,因为许多程序重复使用其他代码进行检查,例如OSX中的以下代码:

http://src.gnu-darwin.org/src/bin/stty/util.c

我认为找到解决方案的最佳方式是挑战的形式。如果有人可以运行以下脚本(理想情况下通过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是战胜挑战的非常有趣的方法,但它们并不能解决问题的核心:)

共有3个答案

况庆
2023-03-14

您总是可以分配伪TTY,这就是屏幕所做的。

在Python中,您可以使用pty.openpty()访问它

此“主”代码通过您的测试:

import subprocess, pty, os

m, s = pty.openpty()
fm = os.fdopen(m, "rw")
p = subprocess.Popen(["python2", "test.py"], stdin=s, stdout=s, stderr=s)
p.communicate()
os.close(s)
print fm.read()

当然,如果您想区分stdin/out/err,您的“从”进程将看到不同的PYT名称:

inp = pty.openpty()
oup = pty.openpty()
erp = pty.openpty()

subprocess.Popen([command, args], stdin=inp[1], stdout=uop[1], stderr=erp[1])
金伟
2023-03-14

对于更简单的用例(例如开发测试),请使用stracelinux)或dtrus(OSX)。当然,这在特权过程中是行不通的。

这是一个示例,您可以区分标准输出fd1和标准输出fd2:

$ strace -ewrite python2 test.py
[snip]
write(1, "This is a real tty :)\n", 22This is a real tty :)
) = 22
write(2, "standard error data", 19standard error data)     = 19
write(1, "standard output data", 20standard output data)    = 20
+++ exited with 0 +++

在上面的示例中,您看到每个标准xxx数据都加倍了,因为您无法重定向stdout/stderr。但是,您可以要求strace将其输出保存到文件中。

从理论上讲,如果stdout和stderr指的是同一个终端,那么您只能在仍然处于进程上下文中的情况下,在用户模式(LD\u预加载)或内核空间(strace工具使用的ptrace接口)下区分2。一旦数据到达实际设备,即伪设备的实设备,区分就会丢失。

吕晟睿
2023-03-14

这样地?

% ./challenge.py >stdout 2>stderr
% cat stdout 
This is a real tty :)
standard output data
% cat stderr 
standard error data

因为我有点作弊

% echo $LD_PRELOAD
/home/karol/preload.so

喜欢所以...

% gcc preload.c -shared -o preload.so -fPIC

我现在觉得很脏,但很有趣。: D

% cat preload.c
#include <stdlib.h>

int isatty(int fd) {
    if(fd == 2 || fd == 1) {
        return 1;
    }
    return 0;
}

char* ttyname(int fd) {
    static char* fake_name = "/dev/fake";
    if(fd == 2 || fd == 1) {
        return fake_name;
    }
    return NULL;
}
 类似资料:
  • 我是一个Linux新手,我正在运行以下Linux命令: 一切都如预期的那样运作;python(2.7)print语句的所有输出都记录在名为my.out的文件中。 唯一的问题是,在运行了几天之后,My.out文件变得非常大。有没有一种方法(在Linux shell级别)可以创建一个新的“my.out”,比方说每天一次,在文件名中带有时间戳?

  • 问题内容: 我想通过python从目录中调用脚本(它们是可执行的shell脚本)。 到现在为止还挺好: 现在我想要的是:假设我有一个具有启动功能的bash脚本。我从那里打电话 回声“某事” 现在,我想看看sys.stdout和退出代码上的回显。我相信您可以使用.communicate()执行此操作,但是我的操作不符合我的预期。 我究竟做错了什么? 任何帮助深表感谢 问题答案: 授予http://d

  • 问题内容: 我想使用apache log4j将日志记录添加到正在开发的应用程序中。在这一点上,我想将所有级别为INFO和更低级别(TRACE,DEBUG)的日志消息重定向到stdout,并将所有其他日志消息从WARN及更高版本(ERROR,FATAL)重定向到stderr。例如: 那么我的log4j.properties文件应该是什么?这是如何看待这一时刻的: 上面配置的问题是logger.err

  • 问题内容: 我想记录stdout和stderr来记录文件,这就是我尝试过的方法。 然后,我想将控制台输出记录在文件中。如 我能做什么? 问题答案: 你提到的不是来自flask的记录器的日志消息中,来自 的记录,你还需要你的处理程序添加到该记录器实例手段,使其工作,如: 如果你查看werkzeug如何初始化其记录器,则将看到只有在尚未设置日志记录的情况下,它才会添加默认处理程序。这意味着,如果你在w

  • 问题内容: 我想执行一个命令,并想重定向stderr和stdout,如下所示: stderr和stdout->仅应在保持顺序的同时写入logs.log文件 stderr->应该打印到SCREEN并也写入errors.log 到目前为止,我可以将它们重定向到屏幕和文件log.txt,如下所示: 但是以上不是我所需要的。 为了更清楚地说明结果是什么。 执行命令后,我只需要在屏幕上看到stderr的结果

  • 我正在尝试编写一个在后台执行控制台命令并侦听StdOut和StdErr的函数。如果出现错误,它会抛出StdErr和StdOut。我正在尝试使用Autohotkey。首先,我尝试使用WScript。Shell COM Object,但StdErr和StdOut始终为空,尽管StdOut绝对应该为非空。关于WScript的信息不多。Shell COM Object和所有Microsoft文档都在技术档