xargs принимает свой стандартный ввод и превращает его в аргументы командной строки.
find . -name '*.c' | xargs grep 'stdlib.h' очень похож на
grep 'stdlib.h' $(find . -name '*.c') # UNSAFE, DON'T USE
И даст те же результаты, пока список имен файлов не слишком длинный для одной командной строки. (Linux поддерживает мегабайты текста в одной командной строке, поэтому обычно вам не нужны xargs.)
Но оба эти сосать, потому что xargs принимает свой стандарт input и превращает его в аргументы командной строки. . Вместо этого работает 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 вы не получите никакого вывода, потому что эти шаблоны не могут совпадать, а поведение по умолчанию для поиска - это AND и вместе.
Замените less в любой точке процесс, чтобы увидеть, какой результат выдает какая-либо часть конвейера.
Дальнейшее чтение: http://mywiki.wooledge.org/BashFAQ имеет отличные вещи.