Можете ли вы обмануть isatty И войти в stdout и stderr отдельно?

проблема

Таким образом, вы хотите регистрировать stdout и stderr (отдельно) процесса или подпроцесса, без вывода результатов, отличных от того, что вы увидите в терминале, если вы ничего не регистрировали.

Кажется довольно просто нет? Ну, к сожалению, кажется, что может быть невозможно написать общее решение для этой проблемы, которое работает на любом данном процессе ...

Фон

Перенаправление каналов - это один из методов разделения stdout и stderr, что позволяет вам регистрировать их по отдельности. К сожалению, если вы измените stdout / err на канал, процесс может обнаружить, что канал не является tty (потому что он не имеет ширины / высоты, скорости передачи и т. Д.) И может соответствующим образом изменить свое поведение. Зачем менять поведение? Ну, некоторые разработчики используют функции терминала, которые не имеют смысла, если вы записываете в файл. Например, для загрузки полос часто требуется, чтобы курсор терминала был перемещен назад к началу строки, а предыдущая строка загрузки была перезаписана полосой новой длины. Также цвет и вес шрифта могут отображаться в терминале, но в плоском файле ASCII они не могут. Если бы вы записали стандартный вывод такой программы непосредственно в файл, этот вывод содержал бы все escape-коды терминала ANSI, а не должным образом отформатированный вывод. Поэтому разработчик реализует какую-то проверку «isatty» перед записью чего-либо в stdout / err, поэтому он может дать более простой вывод для файлов, если эта проверка возвращает false.

Обычное решение здесь состоит в том, чтобы обманом заставить такие программы думать, что каналы на самом деле являются ttys, используя pty - двунаправленный канал, который также имеет ширину, высоту и т. Д. Вы перенаправляете все входы / выходы процесса в этот pty, и это обманывает процесс, заставляющий думать, что он разговаривает с реальным терминалом (и вы можете записать его прямо в файл). Единственная проблема в том, что, используя один pty для stdout и stderr, мы больше не можем различать эти два.

Таким образом, вы можете попробовать разные pty для каждого канала - один для стандартного ввода, один для стандартного вывода и один для stderr. Хотя это будет работать в 50% случаев, многие процессы, к сожалению, проводят дополнительные проверки перенаправления, чтобы убедиться, что выходные пути stdout и stderr (/ dev / tty000x) совпадают. Если это не так, должно быть перенаправление, таким образом, они дают вам такое же поведение, как если бы вы передавали stderr и stdout без pty.

Вы можете подумать, что эта чрезмерная проверка на перенаправление является редкостью, но, к сожалению, на самом деле она довольно распространена, потому что многие программы повторно используют другой код для проверки, как этот фрагмент кода, найденный в 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 - очень интересные способы справиться с этой задачей, но они не решают суть проблемы :)

Ответы на вопрос(5)

Ваш ответ на вопрос