Я не понимаю, как данные передаются по конвейеру, и надеюсь, что кто-то сможет прояснить, что там происходит.
Я думал, что конвейер команд обрабатывает файлы (текст, массивы строк) построчно. (Если каждая команда работает построчно.) Каждая строка текста проходит через конвейер, команды не ждут, пока предыдущая закончит обработку всего ввода.
Но, похоже, это не так.
Вот тестовый пример. Есть несколько строк текста. Я пишу их прописными буквами и повторяю каждую строку дважды. Я делаю это с cat text | tr '[:lower:]' '[:upper:]' | sed 'p'
.
Чтобы следить за процессом, мы можем запустить его «в интерактивном режиме» - пропустите входное имя файла в cat
. Каждая часть трубопровода проходит построчно:
$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2
Но полный конвейер действительно ждет, пока я закончу ввод, EOF
и только затем напечатает результат:
$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D
Так и должно быть? Почему это не построчно?
cat
буферизация, пока не закроется стандартный ввод.tr
иsed
делайте строки процессаcat
до того, как stdin закрываетсяОтветы:
Существует общее правило буферизации, которому следует стандартная библиотека ввода-вывода C (
stdio
), которую использует большинство программ unix. Если вывод идет на терминал, он сбрасывается в конце каждой строки; в противном случае он сбрасывается только тогда, когда буфер (8K в моей системе Linux / amd64; может отличаться в вашей) заполнен.Если все ваши утилиты следовали общему правилу, вы увидите выход с задержкой во всех ваших примерах (
cat|sed
,cat|tr
иcat|tr|sed
). Но есть исключение: GNUcat
никогда не буферизует свой вывод. Он либо не использует,stdio
либо изменяетstdio
политику буферизации по умолчанию .Я могу быть уверен, что вы используете GNU,
cat
а не какой-то другой Unix,cat
потому что другие не будут себя так вести. Традиционный Unixcat
имеет-u
возможность запрашивать небуферизованный вывод. GNUcat
игнорирует эту-u
опцию, потому что ее вывод всегда небуферизован.Поэтому всякий раз, когда у вас есть канал с символом
cat
слева, в системе GNU прохождение данных через канал не будет задерживаться.cat
Даже не собирается построчно - ваш терминал делает. Пока вы вводите данные для cat, ваш терминал находится в «каноническом» режиме - на основе строки, с клавишами редактирования, такими как backspace и ctrl-U, которые дают вам возможность редактировать введенную вами строку перед отправкой Enter.В этом
cat|tr|sed
примереtr
он по-прежнему получает данныеcat
сразу после нажатия Enter, ноtr
придерживаетсяstdio
политики по умолчанию: его выходные данные отправляются в канал, поэтому он не сбрасывается после каждой строки. Он записывает во второй канал, когда буфер заполнен или когда получен EOF, в зависимости от того, что наступит раньше.sed
также следуетstdio
политике по умолчанию, но его выходные данные отправляются на терминал, поэтому он будет записывать каждую строку, как только закончил с ней. Это влияет на то, сколько вы должны набрать, прежде чем что-то появится на другом конце конвейера - если быsed
блок-буферизировал свой вывод, вам пришлось бы печатать вдвое больше (чтобы заполнитьtr
выходной буфер иsed
выходные данные буфер).У GNU
sed
есть-u
опция, поэтому, если вы измените порядок и используете,cat|sed -u|tr
вы увидите, что вывод снова появится мгновенно. (sed -u
Опция может быть доступна в другом месте, но я не думаю, что это древняя традиция Unix, какcat -u
) Насколько я могу судить, нет эквивалентной опции дляtr
.Существует утилита,
stdbuf
которая позволяет вам изменять режим буферизации любой команды, которая используетstdio
значения по умолчанию. Это немного хрупко, поскольку она используетLD_PRELOAD
для достижения чего-то, что библиотека C не была разработана для поддержки, но в этом случае, похоже, работает:источник
tee
аdd
также обычно играют по своим правилам. При творческом объединении эти три инструмента могут практически полностью устранить любые потребностиstdbuf
в фоновых трубопроводах.На самом деле это заставило меня задуматься и еще больше ответить. Отличный вопрос (я опишу его дальше).
Вы забыли попробовать
tr | sed
свои отладочные элементы выше:Так что видимо
tr
буферы. Узнавайте что-то новое каждый день!РЕДАКТИРОВАТЬ :
Пока я думаю об этом, мы выделили причину, но не дали объяснения. Если
cat | tr
он пишет сразу же, если выcat | sed
, он пишет сразу, но если выtr | sed
, она ждет заEOF
. Я хотел бы предложить ответ может быть похоронен вtr
илиsed
исходный код , то и не быть проблемой трубы.РЕДАКТИРОВАТЬ :
Я вижу, что Wumpus предоставил объяснение, когда я печатал последнее редактирование. Благодарность!
источник
stdbuf
который также может быть полезным. unix.stackexchange.com/questions/182537/…