Почему ps o/p перечисляет процесс grep после канала?

Когда я делаю

$ ps -ef | grep cron

я получил

root      1036     1  0 Jul28 ?        00:00:00 cron
abc    21025 14334  0 19:15 pts/2    00:00:00 grep --color=auto cron

Мой вопрос, почему я вижу вторую строку. Насколько я понимаю, ps перечисляет процессы и передает список grep. grep даже не начал работать, пока ps перечисляет процессы, тогда почему grep процесс указан в o/p ?

Связанный второй вопрос:

Когда я делаю

$ ps -ef | grep [c]ron

я получаю только

root      1036     1  0 Jul28 ?        00:00:00 cron

В чем разница между первым и вторым исполнением grep?


person Ankur Agarwal    schedule 01.08.2011    source источник
comment
Вы будете удивлены результатом sleep 3 | sleep 3 | sleep 3 ;-)   -  person Alfe    schedule 17.09.2015


Ответы (7)


Когда вы выполняете команду:

ps -ef | grep cron

оболочка, которую вы используете

(... Я предполагаю, что в вашем случае bash, из-за атрибута цвета grep я думаю, что вы используете систему gnu, такую ​​​​как дистрибутив Linux, но то же самое и в других unix/оболочках...)

выполнит вызов pipe() для создания FIFO, затем fork() (создаст свою текущую копию). Это создаст новый дочерний процесс. Этот новый сгенерированный дочерний процесс будет close() использовать свой стандартный дескриптор выходного файла (fd 1) и присоединит fd 1 к стороне записи канала, созданного родительским процессом (оболочкой, в которой вы выполнили команду). Это возможно, потому что системный вызов fork() будет поддерживать для каждого допустимый дескриптор открытого файла (в данном случае канал fd). После этого это будет exec() первая (в вашем случае) ps команда, найденная в вашей переменной окружения PATH. С вызовом exec() процесс станет командой, которую вы выполнили.

Итак, теперь у вас есть процесс оболочки с дочерним элементом, который в вашем случае является командой ps с атрибутами -ef.

В этот момент родитель (оболочка) снова fork()s. Этот недавно сгенерированный дочерний процесс close()s использует свой стандартный дескриптор входного файла (fd 0) и прикрепляет fd 0 к стороне чтения канала, созданного родительским процессом (оболочкой, в которой вы выполнили команду).

После этого будет exec() первая (в вашем случае) grep команда, найденная в вашей переменной окружения PATH.

Теперь у вас есть процесс оболочки с двумя дочерними процессами (родственными), где первый — это команда ps с атрибутами -ef, а второй — команда grep с атрибутом cron. Сторона чтения канала подключена к STDIN команды grep, а сторона записи подключена к STDOUT команды ps: стандартный вывод команды ps подключен к стандартному вводу команды grep.

Поскольку ps написан для отправки на стандартный вывод информации о каждом запущенном процессе, а grep написан для получения на стандартный ввод чего-то, что должно соответствовать заданному шаблону, вы получите ответ на свой первый вопрос:

  1. оболочка работает: ps -ef;
  2. оболочка работает: grep cron;
  3. ps отправляет данные (даже содержащие строку "grep cron") на grep
  4. grep соответствует своему шаблону поиска из STDIN и соответствует строке "grep cron" из-за атрибута "cron", который вы передали в grep: вы указываете grep сопоставлять строку "cron", и это происходит, потому что "grep cron" строка, возвращаемая ps в момент начала выполнения grep.

Когда вы выполняете:

ps -ef | grep '[c]ron'

переданный атрибут указывает grep сопоставить что-то, содержащее «c», за которым следует «ron». Как и в первом примере, но в этом случае он нарушит строку соответствия, возвращаемую ps, потому что:

  1. оболочка работает: ps -ef;
  2. оболочка работает: grep [c]ron;
  3. ps отправляет данные (даже содержащие строку grep [c]ron) на grep
  4. grep не соответствует шаблону поиска из стандартного ввода, потому что строка, содержащая «c», за которой следует «ron», не найдена, но найдена строка, содержащая «c», за которой следует «]ron».

GNU grep не имеет ограничения на соответствие строк, а на некоторых платформах (я думаю, Solaris, HPUX, aix) ограничение на количество строк задается переменной "$COLUMN" или шириной экрана терминала.

Надеемся, что этот длинный ответ немного прояснит процесс оболочки.

НАКОНЕЧНИК:

ps -ef | grep cron | grep -v grep
person dAm2K    schedule 14.03.2012
comment
Спасибо за уточнение ответа @Ben Jackson. - person Ankur Agarwal; 17.03.2012
comment
Я думаю, что это будет хорошей иллюстрацией к этому замечательному ответу: $ ps aux | grep grep | grep grep | grep grep | grep grep. Вы увидите четыре строки grep grep — все те, что находятся в созданной вами трубе. - person Esmu Igors; 28.07.2020

Оболочка строит ваш конвейер с серией вызовов fork(), pipe() и exec(). В зависимости от оболочки любая ее часть может быть построена первой. Таким образом, grep может быть запущен еще до того, как ps запустится. Или, даже если ps запустится первым, он будет записывать в буфер канала ядра 4k и в конечном итоге заблокируется (при печати строки вывода процесса) до тех пор, пока grep не запустится и не начнет потреблять данные в канале. В последнем случае, если ps может начаться и закончиться раньше, чем grep, вы можете не увидеть grep cron в выводе. Возможно, вы уже заметили этот недетерминизм в игре.

person Ben Jackson    schedule 01.08.2011
comment
Если ps блокирует, то листинг не будет содержать grep..correct. Но Игнасио, кажется, предполагает, что grep должен работать. Я немного смущен. - person Ankur Agarwal; 01.08.2011
comment
На самом деле я не видел упомянутого вами недетерминизма! Но довольно интересно это отметить. - person Ankur Agarwal; 01.08.2011

В вашей команде

ps -ef | grep 'cron'

Linux выполняет команду «grep» перед командой ps -ef. Затем Linux сопоставляет стандартный вывод (STDOUT) «ps -ef» со стандартным вводом (STDIN) команды grep.

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

Изменить Что касается вашего второго вопроса:

В grep (и большинстве движков регулярных выражений) вы можете указать скобки, чтобы сообщить, что вы принимаете ЛЮБОЙ символ в скобках. Таким образом, написание [c] означает, что он будет принимать любой символ, но указан только c. Точно так же вы можете сделать любую другую комбинацию символов.

ps aux | grep cron
root      1079  0.0  0.0  18976  1032 ?        Ss   Mar08   0:00 cron
root     23744  0.0  0.0  14564   900 pts/0    S+   21:13   0:00 grep --color=auto cron

^ Это соответствует самому себе, потому что ваша собственная команда содержит "cron"

ps aux | grep [c]ron
root      1079  0.0  0.0  18976  1032 ?        Ss   Mar08   0:00 cron

Это соответствует cron, потому что cron содержит c, а затем «ron». Однако это не соответствует вашему запросу, потому что ваш запрос [c]ron

Вы можете поместить в скобки все, что хотите, если оно содержит c:

ps aux | grep [cbcdefadq]ron
root      1079  0.0  0.0  18976  1032 ?        Ss   Mar08   0:00 cron

Если вы удалите C, он не будет совпадать, потому что «cron» начинается с c:

ps aux | grep [abedf]ron

^ Нет результатов

Изменить 2

Повторю еще раз: с помощью grep можно делать всевозможные сумасшедшие вещи. Нет смысла выбирать первого персонажа для этого.

ps aux | grep [c][ro][ro][n]
root      1079  0.0  0.0  18976  1032 ?        Ss   Mar08   0:00 cron
person GoldenNewby    schedule 13.03.2012
comment
Я только что добавил еще одну часть вопроса. Мне пришло в голову, что было трудно читать заметки о наградах. Спасибо. - person Ankur Agarwal; 13.03.2012
comment
Бен Джексон (ниже), кажется, предполагает, что ps может быть запущен до grep и записи данных в канал ядра. - person Ankur Agarwal; 13.03.2012
comment
Насколько я знаю, кто из них начнет первым, не имеет значения. Операционная система не обязательно выделяет процессорное время любому из них, пока STDOUT PS не будет отображен на STDIN GREP. - person GoldenNewby; 13.03.2012

Вы написали: «Насколько я понимаю, ps перечисляет процессы и передает список в grep. grep даже не запустился, пока ps перечисляет процессы».

Ваше понимание неверно.

Трубопровод так не работает. Оболочка не выполняет первую команду до завершения, запоминает вывод первой команды, а затем после запускает следующую команду, используя эти данные в качестве входных данных. Нет. Вместо этого выполняются оба процесса, и их входы/выходы подключены. Как писал Бен Джексон, нет никаких особых гарантий того, что процессы будут выполняться одновременно, если они оба очень недолговечны и если ядро ​​может комфортно управлять небольшим объемом данных, проходящих через соединение. В таком случае все действительно могло произойти так, как вы ожидаете, только случайно. Но концептуальная модель, о которой следует помнить, заключается в том, что они работают параллельно.

Если вам нужны официальные источники, как насчет справочной страницы bash:

  A pipeline is a sequence of one or more commands separated by the character |.  The format for a pipeline is:

         [time [-p]] [ ! ] command [ | command2 ... ]

  The  standard  output  of command is connected via a pipe to the standard input of command2.  This connection is
  performed before any redirections specified by the command (see REDIRECTION below).

  ...

  Each command in a pipeline is executed as a separate process (i.e., in a subshell).

Что касается вашего второго вопроса (который, к сожалению, вообще не связан с этим), вы просто описываете особенность работы регулярных выражений. Регулярное выражение cron соответствует строке cron. Регулярное выражение [c]ron не соответствует строке [c]ron. Таким образом, первая команда grep найдет себя в списке процессов, а вторая — нет.

person Zac Thompson    schedule 15.03.2012

На ваш фактический вопрос ответили другие, но я дам совет: если вы не хотите видеть процесс grep в списке, вы можете сделать это следующим образом:

$ ps -ef | grep [c]ron
person Michael Berkowski    schedule 01.08.2011
comment
Спасибо, но у меня были дополнительные сомнения по поводу ответов ниже. Пожалуйста, смотрите мои комментарии. - person Ankur Agarwal; 01.08.2011
comment
Почему при использовании grep [c]ron не отображается процесс grep, тогда как при использовании grep cron всегда отображается процесс grep? На что влияет скобочное выражение. Не могли бы вы уточнить? - person Ankur Agarwal; 13.03.2012
comment
@abc Это работает, потому что регулярное выражение grep точно соответствует c, за которым следует ron, но вывод ps будет отображать буквально grep [c]ron, поскольку это была введенная команда. Следовательно, выражение grep не соответствует ему и отфильтровывает его. - person Michael Berkowski; 13.03.2012
comment
Это требует большего количества цитат; сделайте его grep '[c]ron', иначе он станет grep cron, если вы запустите его в каталоге, содержащем файл с именем cron (поскольку оболочка заменяет все, что выглядит как глобус, списком файлов, до которых он расширяется... и это если вы' вам повезло, и это на настройках по умолчанию; с nullglob он просто станет grep без аргументов, с failglob станет ошибкой). - person Charles Duffy; 30.03.2018

pgrep иногда лучше, чем ps -ef | grep word, потому что исключает grep. Пытаться

pgrep -f bash
pgrep -lf bash
person Felipe Alvarez    schedule 25.11.2013

$ ps -ef | grep cron

Linux Shell всегда выполняет команду справа налево. Итак, перед выполнением ps -ef grep cron уже выполняется, поэтому o/p показывает саму команду.

$ ps -ef | grep [c]ron

Но в этом u указано grep ron, за которым следует только c. Итак, o/p без командной строки, потому что в команде есть [c]ron.

person Sudhir Meena    schedule 11.11.2013