Что делает "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'
Есть несколько причин использовать его вместо других опций, которые изначально не упоминались в других ответах:
find . -name '*.c' -exec grep 'stdlib.h' {}\;
будет генерировать одинgrep
процесс для каждого файла - это обычно считается плохой практикой и может создать большую нагрузку на систему, если найдено много файлов.- Если файлов много,
grep 'stdlib.h' $(find . -name '*.c')
команда, скорее всего, потерпит неудачу, потому что вывод$(...)
операция превысит максимальную длину командной строки оболочки
Как уже упоминалось в других ответах, причина использования -print0
аргумент find
в этом сценарии и -0
Аргументом xargs является то, что имена файлов с определенными символами (например, кавычки, пробелы или даже символы новой строки) по-прежнему обрабатываются правильно.