Могу ли я захватить stdout / stderr отдельно и сохранить исходный порядок?

Я написал приложение для Windows, используя собственный Win32 API. Мое приложение запустит другие процессы, захватит вывод и выделит вывод stderr красным цветом.

Для этого я создаю отдельный канал для stdout и stderr и использую их в структуре STARTUPINFO при вызове CreateProcess. Затем я запускаю отдельный поток для каждого дескриптора stdout / stderr, который читает из канала и записывает вывод в окно.

В большинстве случаев это работает нормально. Проблема, с которой я сталкиваюсь, заключается в том, что если дочерний процесс записывается в stderr и stdout в быстрой последовательности, мое приложение иногда отображает вывод в неправильном порядке. Я предполагаю, что это связано с использованием двух потоков для чтения из каждого дескриптора.

Можно ли захватить stdout и stderr в исходном порядке, в котором они были записаны, и при этом различать их?


person flashk    schedule 09.10.2009    source источник


Ответы (6)


Я почти уверен, что это невозможно сделать, если не написать порожденную программу для записи в пакеты и добавления метки времени к каждому. Без этого вы обычно можете запланировать буферизацию в стандартной библиотеке дочернего процесса, поэтому к тому времени, когда они даже будут переданы по конвейеру родительскому процессу, есть большая вероятность, что они уже вышли из строя.

person Jerry Coffin    schedule 10.10.2009
comment
Да, это такой подход, но у меня не хватило смелости сказать оператору, что слишком большое количество голосов против - неизбежное следствие попытки сделать это на открытых форумах. - person Nicholas Jordan; 12.10.2009
comment
Как это может быть правдой? Почему CMD.exe всегда все делает правильно? - person paulm; 30.08.2013
comment
Похоже, это МОЖНО сделать, но невозможно узнать, в какой поток был записан: stackoverflow.com/questions/18529662/ - person paulm; 31.12.2013
comment
@paulm: Это может сработать для некоторых программ при определенных обстоятельствах, но по причинам, указанным выше, это в значительной степени гарантирует, что по крайней мере часть времени он не сработает. - person Jerry Coffin; 31.12.2013
comment
так всегда работает под CMD, если один и тот же поток используется для обоих, то порядок сохраняется - person paulm; 31.12.2013

В большинстве реализаций stdout и stderr, которые я видел, stdout буферизуется, а stderr - нет. В основном это означает, что вам не гарантируется, что они будут в порядке, даже если программа запускается в прямой командной строке.

http://en.wikipedia.org/wiki/Stderr#Standard_error_.28stderr.29

Краткий ответ: вы не можете гарантировать, что читаете строки в том же порядке, в котором они появляются в cmd.exe, потому что порядок, в котором они появляются в cmd.exe, не гарантируется.

person Evan Larkin    schedule 12.10.2009

Не совсем, вы так думаете, но std_out находится под контролем разработчиков системы - как именно и когда будет написан std_out, зависит от системного планировщика, который, по моему тестированию, подчиняется проблемам, которые не так документированы.

Однажды я писал кое-что и поработал на одном из устройств в системе, пока у меня был открытый код в редакторе, и я обнаружил, что система отдает приоритет в реальном времени драйверу, оставляя мой тщательно созданный c- код где-то примерно на одну десятую важнее собственного кода.

Повторное инвертирование, чтобы вы получили последовательное упорядочение записи, будет, мягко говоря, сложной задачей.

person Nicholas Jordan    schedule 09.10.2009

Вы можете перенаправить stderr на stdout:

command_name 2>&1

Насколько я помню, это возможно в C с использованием каналов.

ОБНОВЛЕНИЕ: Ой, извините - пропустил часть о возможности различать эти два. Я знаю, что TextMate каким-то образом сделал это, используя своего рода код, видимый пользователем ... Давно не искал, но я посмотрю. Но после некоторых дополнительных размышлений можно ли использовать в Ruby что-то вроде Open3? Вам нужно будет смотреть и STDOUT, и STDERR одновременно, но на самом деле никто не должен ожидать определенного порядка вывода в отношении этих двух.

ОБНОВЛЕНИЕ 2: Пример того, что я имел в виду в Ruby:

require 'open3'

Open3.popen3('ruby print3.rb') do |stdin, stdout, stderr|
  loop do
    puts stdout.gets
    puts stderr.gets
  end
end

... где print3.rb просто:

loop do
  $stdout.puts 'hello from stdout'
  $stderr.puts 'hello from stderr'
end

Вместо того, чтобы бросать результат прямо в puts, вы можете отправить сообщение наблюдателю, который распечатает его в вашей программе. Извините, у меня нет Windows на этой машине (или какой-либо другой, доступной сейчас), но я надеюсь, что это иллюстрирует концепцию.

person Benjamin Oakes    schedule 10.10.2009

Я почти уверен, что даже если вы не разделяете их вообще, вам все равно не гарантировано, что они будут заменять друг друга в правильном порядке.

person shoosh    schedule 11.10.2009

Поскольку целью является аннотирование вывода существующей программы, любое возможное чередование двух потоков должно быть правильным. Первоначальный разработчик разместит соответствующие вызовы flush (), чтобы обеспечить соблюдение любого обязательного порядка.

Как объяснялось ранее, запишите каждый записанный фрагмент с отметкой времени и используйте ее для восстановления последовательности, фактически наблюдаемой устройствами вывода.

person Pekka    schedule 12.10.2009