Почему некоторые команды не читаются со стандартного ввода?

19

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

Скажем, например, чтобы убить определенный процесс, который обрабатывает PDF-файлы, следующее не будет работать с использованием конвейера:

ps aux | grep pdf | awk '{print $2}'|kill

Вместо этого мы можем сделать это только следующими способами:

kill $(ps aux| grep pdf| awk '{print $2}')

или

ps aux | grep pdf | awk '{print $2}'| xargs kill

Согласно man bash(версия 4.1.2):

The standard output of command is connected via a pipe to the standard input of command2.

Для приведенного выше сценария:

  • стандарт grepявляется стандартным ps. Это работает.
  • стандарт awkявляется стандартным grep. Это работает.
  • стандарт killявляется стандартным awk. Это не работает

Stdin следующей команды всегда получает ввод из stdout предыдущей команды.

  • Почему это не работает с killили rm?
  • Что различались kill, rmвход с grep, awkвходом?
  • Есть ли правила?
sylye
источник
1
Это не ответ , но вы можете иметь взгляд на pgrep, pkillи killallкоманде.
Тердон
2
@terdon: Я просто использую приведенный выше сценарий, чтобы показать проблему с конвейером, я понимаю, что pgrepи остальные могут достичь этого отлично :)
sylye

Ответы:

17

Существует два распространенных способа ввода данных в программы:

  • предоставить данные в STDIN процессов
  • указать аргументы командной строки

killиспользует только аргументы командной строки. Он не читает из STDIN. Программы любят grepи awkчитают из STDIN (если имена файлов не указаны в качестве аргументов командной строки) и обрабатывают данные в соответствии с их аргументами командной строки (шаблон, операторы, флаги, ...).

Вы можете передавать только STDIN других процессов, а не аргументы командной строки.

Общее правило состоит в том, что программы используют STDIN для обработки произвольного количества данных. Все дополнительные входные параметры или, если их вообще мало, передаются аргументами командной строки. Если командная строка может быть очень длинной, например, для длинных awkпрограммных текстов, часто есть возможность прочитать их из дополнительных программных файлов ( -fопция awk).

Чтобы использовать STDOUT программ в качестве аргументов командной строки, используйте $(...)или в случае большого количества данных xargs. findможет также это напрямую с -exec ... {} +.

Для полноты: чтобы записать аргументы командной строки в STDOUT, используйте echo.

jofel
источник
1
Откуда мы знаем, что команда будет принимать только аргументы, но не STDIN? Есть ли систематический или программный способ, вместо того, чтобы угадывать или читать с man-страницы? Читая только справочную страницу, я не мог получить никаких конкретных подсказок, чтобы быть уверенными в том, может ли команда принимать или не принимать STDIN, так как STDIN также является частью аргументов способа представления страницы руководства. Например, gzipв ОПИСАНИИ не говорится, что в качестве входных данных необходимо указать имя файла. Я смотрю, есть ли более систематический способ определить это.
sylye
Существует также аргумент "-", который означает "stdin" (или "stdout") для некоторых команд.
Эммануэль
xargsТочно не позволит вам «передать аргументы командной строки»?
Т. Веррон
@ Т. Веррон, да, это задача xargs. Он вызывает команду при необходимости более одного раза (размер командной строки ограничен) и имеет множество других опций.
Джофель
2
В тексте описания будет описано, как вы можете использовать программу. Например, gzip говорит: «Программа gzip сжимает и распаковывает файлы, используя кодирование Lempel-Ziv (LZ77). Если файлы не указаны, gzip сжимает из стандартного ввода или распаковывает в стандартный вывод». Если на странице руководства не упоминается стандартный ввод, она не будет использоваться.
Алан Шутко
16

Это интересный вопрос, и он касается части философии Unix / Linux.

Итак, в чем разница между программами , такими как grep, sed, sortс одной стороны , и kill, rm, lsс другой стороны? Я вижу два аспекта.

Фильтр аспект

  • Первый вид программ также называется фильтрами . Они принимают входные данные, либо из файла, либо из STDIN, модифицируют его и генерируют некоторый вывод, в основном в STDOUT. Они предназначены для использования в конвейере с другими программами в качестве источников и назначений.

  • Второй вид программ действует на вход, но вывод, который они дают, часто не связан с вводом. killне имеет выхода, когда он работает регулярно, и не делает ls. Просто есть возвращаемое значение, чтобы показать успех. Обычно они не принимают входные данные от STDIN, но в основном выдают выходные данные для STDOUT.

Для подобных программ lsаспект фильтра не работает так хорошо. Конечно, он может иметь вход (но не нужен), и выход тесно связан с этим входом, но он не работает как фильтр. Однако для таких программ другой аспект все еще работает:

Семантический аспект

  • Для фильтров их ввод не имеет семантического значения . Они просто читают данные, изменяют данные, выводят данные. Не имеет значения, является ли это списком числовых значений, некоторыми именами файлов или исходным кодом HTML. Значение этих данных дается только кодом, который вы предоставляете фильтру: регулярное выражение grep, правила awkили программа Perl.

  • Для других программ, таких как killили ls, их ввод имеет значение , обозначение . killожидает номера процессов, lsожидает имена файлов или путей. Они не могут обрабатывать произвольные данные и не предназначены для этого. Многие из них даже не нуждаются ни в каком входе или параметрах, как ps. Они обычно не читают из STDIN.

Вероятно, можно объединить эти два аспекта. Фильтр - это программа, входные данные которой не имеют смыслового значения для программы.

Я уверен, что где-то читал об этой философии, но в данный момент я не помню никаких источников, извините. Если у кого-то есть источники, пожалуйста, не стесняйтесь редактировать.

Dubu
источник
5

Здесь нет «правил» как таковых. Некоторые программы принимают входные данные от STDIN, а некоторые нет. Если программа может принимать входные данные из STDIN, она может быть передана, если нет, то не может.

Обычно вы можете сказать, будет ли программа принимать данные, думая о том, что она делает. Если работа программы заключается в том, чтобы каким-либо образом манипулировать содержимым файла (например grep, sedи awkт. Д.), Она обычно принимает входные данные из STDIN. Если его работа состоит в том, чтобы манипулировать сам файл (например mv, rm, cp) или процесс (например kill, lsof) или для возврата информации о чем - либо (например top, find, ps) , то это не делает.

Другой способ думать об этом - это разница между аргументами и вводом. Например:

mv foo bar

В приведенной выше команде mvнет ввода как такового. То, что было дано, это два аргумента. Он не знает или не заботится о том, что находится в любом из файлов, он просто знает, что это его аргументы, и он должен манипулировать ими.

С другой стороны

sed -e 's/foo/bar/' < file
--- -- ------------   ----
 |   |       |          |-> input
 |   |       |------------> argument        
 |   |--------------------> option/flag/switch
 |------------------------> command

Здесь sedбыл дан ввод, а также аргумент. Так как он принимает ввод, он может читать его из STDIN, и он может быть передан по каналу.

Это становится более сложным, когда аргумент может быть входным. Например

cat file

Вот fileаргумент, который был дан cat. Чтобы быть точным, имя файла fileявляется аргументом. Однако, поскольку catэто программа, которая манипулирует содержимым файлов, ее вход - это то, что находится внутри file.

Это можно проиллюстрировать с straceпомощью программы, которая отслеживает системные вызовы, сделанные процессами. Если мы запустим cat fooчерез strace, мы увидим, что файл fooоткрыт:

$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)     

Первая строка выше показывает, что программа /bin/catбыла вызвана и ее аргументы были catи foo(первым аргументом всегда является сама программа). Позже аргумент fooбыл открыт в режиме только для чтения. Теперь сравните это с

$ strace ls foo 2| grep foo 
execve("/bin/ls", ["ls", "foo"], [/* 44 vars */]) = 0
stat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lstat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(1, "foo\n", 4foo

Вот и lsвзял себя и в fooкачестве аргументов. Однако openвызова нет , аргумент не обрабатывается как ввод. Вместо этого lsвызывает системную statбиблиотеку (которая отличается от statкоманды), чтобы получить информацию о файле foo.

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

Тердон
источник
0
  • Почему это не работает с kill или rm?

killи rmне нужен STDIN.

  • В чем разница между kill, вводом rm с помощью grep, вводом awk?

Для killи rm, пользователи предоставляют свою настраиваемую информацию в качестве аргумента и $(cmd)помогают взять STDOUT cmdи преобразовать ее в информационный аргумент.

Для grepи awkпользователи предоставляют аргументы и, кроме того, также STDINили обычный файл, который будет обрабатываться командой. STDINможет быть передан с конвейером |или вручную.

  • Есть ли правила?

Прочитайте руководство или исходные коды. И если вы не найдете ничего, что вам нужно, вы можете сделать простой, но, возможно, опасный тест:

Просто введите интересующую вас команду с аргументами, которые вы уже поняли, и посмотрите, приостановлена ​​ли команда (ничего не происходит). Если он приостановился, он фактически ожидает STDIN (вы можете попробовать catи echoувидеть другое). Вы вводите вручную, Ctrl-Dи команда идет вперед (показать результаты или ошибки) и возвращает. Такая команда нуждается в STDIN в этой ситуации (с аргументами, которые вы предоставляете).

Одна и та же команда может не нуждаться в STDIN в разных ситуациях (например, catожидает STDIN, но cat file.txtне).

Алекс Хуан
источник