Конвейер в Windows cmd.exe не перенаправляет стандартный вывод до завершения процесса?

Учитывая каналы в командной оболочке Windows cmd.exe:

C:\>feed | filter

Стандартный вывод процесса подачи, похоже, не достигает стандартного ввода процесса фильтрации до тех пор, пока ПОСЛЕ того, как процесс подачи не завершится.

Этот тип «буферизации» может вызвать раздражающие задержки в выводе сообщений для длительных процессов загрузки (где вы можете нажать «ctrl-c», чтобы прервать его при преждевременном сбое).

Есть ли способ избежать этого, чтобы стандартный вывод процесса подачи достиг стандартного ввода процесса фильтрации, как только данные станут доступны? (без буферизации)

Например, следующий упрощенный пример:

Feed.bat:

@echo off
echo something
sleep 3
echo something else

фильтр.bat:

@echo off
for /F "tokens=*" %%a in ('more') do (
    echo _%%a
)

Приведенная ниже команда ничего не отображает до тех пор, пока не пройдет 3 секунды (когда сон завершится):

C:\>feed | filter
_something
_something else

Желаемым поведением было бы то, что печатается «_something», за которым следует 3-секундная задержка, после чего печатается «_something else».


person Brian    schedule 05.02.2014    source источник
comment
В какой версии Windows есть команда SLEEP? Я использую TIMEOUT (или хак PING)   -  person dbenham    schedule 05.02.2014
comment
Это не имеет ничего общего с тем, как работает командный процессор, все, что связано с самой программой. Который автоматически переключает вывод в буферизованный режим, что делает конвейер намного более эффективным. Базовыми функциями CRT являются _isatty() и setvbuf(). Но с новым поведением выходные данные не покидают буфер, пока он не заполнится до отказа. Или программа сама сбрасывает буфер. Чего не хватает в этой программе. Это очень поправимо, если у вас есть исходники программы.   -  person Hans Passant    schedule 05.02.2014


Ответы (1)


Каналы асинхронны в Windows cmd.exe. Они не ждут, пока завершится левая сторона, прежде чем передать информацию правой. Но ваша программа не демонстрирует этого по двум причинам.

1) Команда FOR /F не начинает итерацию строк до тех пор, пока не завершится выполнение команды в предложении IN(). Это справедливо для всех вариантов FOR/F. Весь результат предложения IN() буферизуется перед повторением любых строк.

Таким образом, ваш filter.bat не может продемонстрировать асинхронную природу каналов.

2) Команда MORE не будет записывать неполные строки — она ждет, пока не получит символ новой строки, прежде чем печатать на стандартный вывод. (если он не достигает конца файла).

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


Вот моя версия FEED.BAT - пишет несколько строк с несколькими паузами. Он также пишет три символа без перевода строки с паузой после каждого.

@echo off
echo something
timeout /nobreak 3 >nul
echo something else
timeout /nobreak 3 >nul
for /l %%N in (1 1 3) do (
  <nul set /p "=%%N"
  timeout /nobreak 3 >nul
)
echo(
echo Done

Вот моя версия FILTER.JS - она ​​читает один символ из стандартного ввода и записывает его в стандартный вывод, пока не достигнет конца файла.

while (!WScript.StdIn.AtEndOfStream) WScript.Stdout.Write(WScript.StdIn.Read(1));

И вот команда для проверки поведения

feed | cscript //nologo filter.js

А вот вывод, где <pause> вставляется всякий раз, когда есть пауза перед дальнейшим выводом.

something
<pause>something else
1<pause>2<pause>3<pause>
Done

Мой тест выше демонстрирует, что канал немедленно отправляет любую информацию, которую он получает (при условии, что фильтр готов ее получить).

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

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

person dbenham    schedule 05.02.2014