Почему иногда работают трубопроводы в командные группы BASH?

Scott S спросил: 28 марта 2018 в 02:36 в: linux

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

ps aux | { head -1; grep root; }

Результат будет выглядеть примерно так:

USER               PID  %CPU %MEM      VSZ    RSS   TT  STAT STARTED      TIME COMMAND
root               142   0.0  0.0  1234567   2520   ??  Ss    3:14AM   0:08.03 /usr/sbin/notifyd
root                55   0.0  0.0  7890123   2460   ??  Ss    3:14AM   0:01.94 /usr/sbin/syslogd
...

Однако при использовании с другими программами командной строки вывод не соответствует ожидаемому.

Возьмите следующий пример df.

df -h

Выводит следующее.

Filesystem      Size  Used Avail Use% Mounted on
/dev/disk1s1    466G  103G  362G  22% /
/dev/disk1s4    466G  1.1G  362G   1% /blah/blah/blah

Используя df в аналогичном синтаксисе, как показано выше, с помощью ps.

df -h | { head -1; grep disk1; }

Выводит следующее.

Filesystem      Size  Used Avail Use% Mounted on

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

Почему это отличается от ps?

Я чувствую, что знание этих различий поможет мне лучше понять обработку BASH .

Спасибо!


2 ответа

Barmar ответил: 28 марта 2018 в 02:46

Это потому, что head буферизует свой ввод. Он читает в большой буфер из канала, затем начинает извлекать строки из этого буфера. После того, как он прочитал и напечатал первые N строк, он выходит. Затем grep начинает чтение из канала. Но все, что head уже прочитало в свой буфер, недоступно.

Причина, по которой он работает с ps, заключается в том, что он выдает много выходных данных, что не вписывается в этот буфер. Затем grep может обрабатывать остальную часть вывода. Но я думаю, что если вы проверите внимательно, вы увидите, что результат неполный.

Вывод df намного меньше, все это помещается в буфер, который head, так что grep для обработки ничего не осталось.

Размер буфера, вероятно, примерно равен 4 КБ.

Вы можете делать то, что хотите, с помощью awk:

df -h | awk 'NR == 1 || /disk1/'
ps aux | awk 'NR == 1 || /root/'

NR - это номер строки, поэтому она печатает строку, если это первая строка или она соответствует регулярному выражению.

chepner ответил: 28 марта 2018 в 03:05
Не стоит публиковать ответ как отдельный ответ, но комбинация read hdr; echo "$hdr" также будет работать вместо head -1, поскольку read не выполняет буферизацию, считывая один байт в то время, пока он не увидит новую строку.
Tomi Ollila ответил: 28 марта 2018 в 05:20

Sed (1) также можно использовать для фильтрации вывода в этом случае:

    ps aux | sed -n '1p; /root/p'

-n: не выводить строку ввода на стандартный вывод после того, как все команды были применены к нему.

1p; "адрес" строки1, с "p" для печати пространства шаблона

/root/p; "адрес" из / regexp / соответствия "корня", с "p" для печати пространства шаблона

Альтернатива:

    ps aux | sed '1p; /root/p; d;'

В некоторых системах может потребоваться ps -aux тире (-) для префикса параметров. Системы Linux и BSD этого не делают (не могу быть уверенным, как ведет себя macOS, у меня нет такой системы для проверки).