Нужны объяснения от опытных пользователей для такого непредсказуемого поведения:
ps -eF | { head -n 1;grep worker; }
UID PID PPID C SZ RSS PSR STIME TTY TIME CMD
root 441 2 0 0 0 2 paź15 ? 00:00:00 [kworker/2:1H]
все выглядит хорошо, тогда как
ls -la / | { head -n 1;grep sbin; }
отображает только вывод из head
... Я думал stdout 2>&1
и не работает ни для меня, это странно, какие-либо объяснения или предложить, как справиться с этим?
head
иgrep
там ничего не делать.Ответы:
Я провел некоторые исследования с использованием,
strace
и, похоже, это связано с тем, как программа в левой части конвейера выполняет запись в терминал. Когдаls
команда выполняется, она записывает все данные в одинwrite()
. Это приводитhead
к потреблению всего стандартного ввода.С другой стороны
ps
, данные записываются в пакетном режиме, поэтому только первое изwrite()
них используетсяhead
, а затем оно существует. Позже звонкиwrite()
перейдут к недавно порожденномуgrep
процессу.Это означает, что он не будет работать, если процесс, к которому вы пытаетесь
grep
подключиться, не будет выполнен в первую очередьwrite()
, посколькуgrep
он не может увидеть все данные (он видит даже меньше, чем просто данные без первой строки).Вот пример попытки выполнить поиск pid 1 в моей системе:
Ваш
ps -eF
пример работает только случайно.источник
write()
звонков. Если бы онhead
медленно выполнял свойread()
вызов (так, чтобы в буфере канала были все данные), он бы демонстрировал одинаковое поведение для обоихls
иps
.Это вызвано буферизацией в glibc. В случае
ls
вывода выводится в один внутренний буфер и как таковой передается просто вhead
. Дляps -eF
, выход больше, и, как только онhead
заканчивается, следующийgrep
получает оставшиеся части (но не весь) выводps
.Вы можете избавиться от него, сняв буферизацию канала - например, с помощью
sed -u
(я не уверен, что это не расширение GNU):источник
Происходит то, что
head -n 1
читает больше 1 строки. Для оптимальной пропускной способности head считывает куски байтов, поэтому он может читать по 1024 байта за раз, а затем просматривать эти байты на предмет разрыва первой строки. Поскольку разрыв строки может произойти в середине этих 1024 байтов, остальные данные будут потеряны. Его нельзя вернуть на трубу. Таким образом, следующий процесс, который выполняется только получает байты 1025 и далее.Ваша первая команда выполнена успешно, потому что
kworker
процесс идет после того первого чанка, которыйhead
читает.Для того, чтобы это работало,
head
придется читать по 1 символу за раз. Но это очень медленно, так что нет.Единственный способ сделать что-то подобное эффективно - сделать так, чтобы один и тот же процесс выполнял и "head", и "grep".
Вот 2 способа сделать это:
или
Есть намного больше ...
источник
Если вам нужна только первая или две строки, уловка следующего типа работает и позволяет избежать проблем с буферизацией, вызванных использованием двух разных команд для чтения выходного потока:
Он
read
встроен в оболочку и не использует весь буфер ввода только для вывода одной строки, поэтому использованиеread
оставляет все остальные выходные данные для следующей команды.Если вы хотите подчеркнуть проблемы буферизации, показанные в ваших примерах, в которых используются две разные команды, добавьте
sleep
к ним a, чтобы устранить проблемы с синхронизацией, и разрешите команде слева сгенерировать все свои выходные данные, прежде чем команды справа попытаются прочитать любую из Это:Теперь оба приведенных выше примера завершаются сбоем одинаково -
head
чтение всего буфера вывода производится только для создания одной строки, и этот буфер недоступен для следующегоgrep
.Вы можете увидеть проблему буферизации еще яснее, используя несколько примеров, которые нумеруют выходные строки, чтобы вы могли определить, какие строки отсутствуют:
Простой способ увидеть проблему буферизации - использовать
seq
список, который генерирует список. Мы можем легко определить, какие цифры пропали без вести:Мое хитрое решение, использующее оболочку для чтения и вывода первой строки, работает правильно даже с добавленной задержкой сна:
Ниже приведен полный пример, показывающий
head
проблемы с буферизацией, показывающий, какhead
расходуется весь буфер вывода только для того, чтобы каждый раз создавать его пять строк. Этот использованный буфер недоступен для следующейhead
команды в последовательности:Глядя на число
1861
выше, мы можем вычислить размер используемого буфера,head
посчитавseq
выходные данные от1
до1860
:Мы видим, что
head
буферизация происходит за счет считывания целых 8 КБ (8 * 1024 байт) выходного потока канала за раз, даже для получения только нескольких строк собственного вывода.источник