Как отменить буфер stdout при передаче команд в cmd.exe под Windows?

1394
szr

Учитывая следующее:

C:\>perl -E " say STDOUT 111; say STDERR 222; say STDOUT 333; " 111 222 333  C:\>perl -E " say STDOUT 111; say STDERR 222; say STDOUT 333; " | cat 222 111 333 

Порядок вывода не сохраняется при передаче команд из-за буферизации stdout и небуферизации stderr. Как надежно отключить буферизацию stdout, чтобы порядок был сохранен при передаче команд?

Я искал все, пытаясь найти решения, я пробовал скрипты, как unbufferиз expectпакета в Cygwin и Linux, но это работает только в Cygwin (за ее пределами, в голой земле cmd.exe, порядок все еще неправильный, stderr все еще приходит раньше, чем ожидалось.) То же самое сstdbuf -i0 -o0 -e0 ...

Любая помощь будет принята с благодарностью.

2
Проблема на самом деле не в буферизации, а в перенаправлении: тексты `stdout 'задерживаются по конвейеру в` cat`, тогда как тексты в `stderr` пишутся непосредственно в терминал по мере их создания. Самым простым решением было бы перенаправить `stderr` на` stdout` перед передачей, как в `... 2> & 1 | cat`. Нет никакого способа гарантировать, что запуск `cat` прервет выполнение` perl`. AFH 8 лет назад 0
@AFH Спасибо, но я тоже это уже пробовал, но все равно выходит из строя: `C: \> perl -E" говорят STDOUT 111; говорят STDERR 222; говорят STDOUT 333; "2> & 1 | cat` => `222 111 333` szr 8 лет назад 0
Я бы этого не ожидал: без `perl` в Windows это не так просто проверить. Я предполагаю, что это потому, что оба выходных потока буферизируются, и вывод генерируется только тогда, когда оба они закрыты, и в этом случае порядок зависит от порядка закрытия файла. В C-программе вы можете установить выходные потоки как небуферизованные или запрограммировать `fflush` после каждого` fputs`, но я не знаю, возможно ли это в `perl` - возможно, вызвать функцию C для этого? AFH 8 лет назад 0

1 ответ на вопрос

0
ShadowRanger

Буфер слоев ввода / вывода по умолчанию в Perl, и по умолчанию его не используют stdio, поэтому unbufferи stdbuf(которые изменяют stdioбуферизацию по умолчанию ) не работают.

Perl предоставляет свой собственный способ управления используемыми уровнями ввода-вывода: PERLIOПеременная окружения. За в man perlrunдокументации, должна быть возможность запускать set PERLIO=:unixперед запуском команды, или для сырых родной Windows, обрабатывать на основе ввода / вывода, которые все еще могут быть экспериментальным / глючит, set PERLIO=:win32. Либо следует обойти нормальное поведение буферизации, перейдя прямо к необработанным системным вызовам.

Если предположить, что catсам по себе небуферизован (я полагаю, что он использует необработанные операции чтения и записи без буферизации, так и должно быть), это все равно не гарантирует желаемого поведения. Perl может отправить данные catнемедленно, но если не catудастся прочитать их и выписать обратно быстрее, чем Perl сможет перейти к следующей строке и распечатать STDERR, вы все равно STDERRсначала увидите вывод. В локальных тестах (в Linux, но они должны быть очень похожими) с помощью PERLIO=:unixpiping to catя увидел следующие результаты:

222111  333 

затем:

222 111 333 

затем:

111222  333 

затем (дважды подряд):

111 222 333 

Дело в том, что даже при буферизации вне уравнения трубопровод вводит условия гонки из-за параллелизма на уровне процесса. Единственный способ обойти это - убедиться, что оба потока идут cat:

perl -E "say STDOUT 111; say STDERR 222; say STDOUT 333;" 2>&1 | cat 

последовательно выводит:

111 222 333 

потому что все данные отправляются catнепосредственно перед выполнением следующего print. Единственный другой способ получить (в основном) надежный заказ - это спать между отпечатками (что позволяет catвыиграть гонку perl).