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

Некоторое время я использовал следующую команду, чтобы сохранить заголовки на выходе 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.

Спасибо!

Это больше вопрос библиотеки C и буферизации ввода-вывода. Во втором случае head -1 считывает буфер данных со стандартного ввода и распечатывает первую строку; остальное было проигнорировано. grep не смог увидеть данные, которые читал head, и, по-видимому, не осталось ничего значимого. В первом случае не совсем понятно, что дает, но, вероятно, поведение было аналогичным, за исключением того, что на выходе ps aux было больше строк данных после того, как head прочитал первое заполнение буфера, поэтому grep выдал те строки, которые совпали после отсутствия неизвестное количество строк, прочитанных head.

Jonathan Leffler 21.04.2018 18:24

Stack Overflow - это сайт для вопросов по программированию и разработке. Этот вопрос кажется не по теме, потому что он не о программировании или разработке. См. О каких темах я могу спросить здесь в Справочном центре. Возможно, лучше спросить Суперпользователь или Обмен стеков Unix и Linux.

jww 22.04.2018 02:10
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
52
2

Ответы 2

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

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

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

Размер буфера, вероятно, составляет около 4К символов.

С awk вы можете делать все, что хотите:

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

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

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

chepner 21.04.2018 18:45

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 i.t. тире (-) перед опциями префикса. Системы Linux и * BSD этого не делают (не могу точно сказать, как себя ведет macOS, у меня нет такой системы, чтобы проверить).

Другие вопросы по теме