Что делает "xargs grep"?

Я знаю grep команда, и я узнаю о функциональности xargs, поэтому я прочитал эту страницу, которая дает несколько примеров о том, как использовать xargs команда.

Я запутался в последнем примере, пример 10. Он говорит: "Команда xargs выполняет команду grep, чтобы найти все файлы (среди файлов, предоставляемых командой find), которые содержали строку" stdlib.h ""

$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...

Тем не менее, в чем разница с простым использованием

$ find . -name '*.c' | grep 'stdlib.h'

?

Очевидно, я все еще борюсь с тем, что именно делает xargs, поэтому любая помощь приветствуется!

4 ответа

Решение
$ find . -name '*.c' | grep 'stdlib.h'

Этот канал выводит (stdout)* из find к (стандарт) * grep 'stdlib.h' как текст (т.е. имена файлов рассматриваются как текст). grep выполняет свои обычные функции и находит соответствующие строки в этом тексте (любые имена файлов, которые сами содержат шаблон). Содержимое файлов никогда не читается.

$ find . -name '*.c' | xargs grep 'stdlib.h'

Это создает команду grep 'stdlib.h' к которому каждый результат из find является аргументом - поэтому он будет искать совпадения внутри каждого файла, найденного find (xargs можно рассматривать как превращение его стандартного ввода в аргументы данных команд) *

использование -type f в вашей команде поиска, или вы получите ошибки от grep для сопоставления каталогов. Кроме того, если имена файлов имеют пробелы, xargs будет плохо, так что используйте нулевой разделитель, добавив -print0 а также xargs -0 для более надежных результатов:

find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

* добавил эти дополнительные пояснительные замечания, как предложено в комментарии @cat

xargs берет свой стандартный ввод и превращает его в аргументы командной строки.

find . -name '*.c' | xargs grep 'stdlib.h' очень похоже на

grep 'stdlib.h' $(find . -name '*.c')  # UNSAFE, DON'T USE

И даст те же результаты, пока список имен файлов не слишком длинный для одной командной строки. (Linux поддерживает мегабайты текста в одной командной строке, поэтому обычно вам не нужны xargs.)


Но оба они отстой, потому что они ломаются, если ваши имена файлов содержат пробелы. Вместо, find -print0 | xargs -0 работает, но так же

find . -name '*.c' -exec grep 'stdlib.h' {} +

Это никогда нигде не транслирует имена файлов: find упаковывает их в большую командную строку и запускает grep непосредственно.

\; вместо + запускает grep отдельно для каждого файла, что намного медленнее. Не делай этого. Но + это расширение GNU, так что вам нужно xargs сделать это эффективно, если вы не можете предположить, что GNU find.


Если вы пропустите xargs , find | grep сопоставляет свой шаблон со списком имен файлов, которые find печать.

Так что в этот момент вы могли бы просто сделать find -name stdlib.h, Конечно, с -name '*.c' -name stdlib.h вы не получите никакого вывода, потому что эти шаблоны не могут как совпадать, так и найти, что поведение по умолчанию - это И правила вместе.

Замена less в любой точке процесса, чтобы увидеть, какую продукцию производит любая часть конвейера.


Дальнейшее чтение: http://mywiki.wooledge.org/BashFAQ имеет несколько замечательных вещей.

В общем, xargs используется для случаев, когда вы бы трубу (с символом |) что-то от одной команды к другой (Command1 | Command2), но выходные данные из первой команды неправильно принимаются в качестве входных данных для второй команды.

Обычно это происходит, когда вторая команда не обрабатывает ввод данных через стандартный ввод (stdin) правильно (например: несколько строк в качестве ввода, способ установки строк, символы, используемые в качестве ввода, несколько параметров в качестве ввода, тип данных, полученный как вход и т. д.). Чтобы дать вам быстрый пример, протестируйте следующее:

Пример 1:

ls | echo - Это ничего не сделает, так как echo не знает, как обрабатывать ввод, который он получает. Теперь в этом случае, если мы используем xargs он будет обрабатывать ввод таким способом, который может быть правильно обработан echo (например: как одна строка информации)

ls | xargs echo - Это выведет всю информацию из ls в одну строку

Пример 2:

Допустим, у меня есть несколько файлов goLang в папке с именем go. Я бы посмотрел на них примерно так:

find go -name *.go -type f | echo - Но если символ трубы там и echo в конце концов, это не сработает.

find go -name *.go -type f | xargs echo - Здесь это будет работать благодаря xargs но если бы я хотел каждый ответ от find Команда в одну строку, я бы сделал следующее:

find go -name *.go -type f | xargs -0 echo - В этом случае, тот же выход из find будет показано echo,

Команды как cp, echo, rm, less и другие, которые нуждаются в лучшем способе обработки ввода, получают выгоду при использовании с xargs,

xargs используется для автоматической генерации аргументов командной строки на основе (обычно) списка файлов.

Таким образом, рассматривая некоторые альтернативы использованию следующих xargs команда:

find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

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

  1. find . -name '*.c' -exec grep 'stdlib.h' {}\; будет генерировать один grep процесс для каждого файла - это обычно считается плохой практикой и может создать большую нагрузку на систему, если найдено много файлов.
  2. Если файлов много, grep 'stdlib.h' $(find . -name '*.c') команда, скорее всего, потерпит неудачу, потому что вывод $(...) операция превысит максимальную длину командной строки оболочки

Как уже упоминалось в других ответах, причина использования -print0 аргумент find в этом сценарии и -0 Аргументом xargs является то, что имена файлов с определенными символами (например, кавычки, пробелы или даже символы новой строки) по-прежнему обрабатываются правильно.

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