O xargs pega sua entrada padrão e a transforma em argumentos da linha de comando.
find . -name '*.c' | xargs grep 'stdlib.h' é muito parecido com
grep 'stdlib.h' $(find . -name '*.c') # UNSAFE, DON'T USE
E fornecerá os mesmos resultados, desde que a lista de nomes de arquivos não seja muito longa para uma única linha de comando. (O Linux suporta megabytes de texto em uma única linha de comando; portanto, geralmente você não precisa de xargs.)
Mas ambos são péssimos, porque quebram se os nomes de arquivos contiverem espaços . Em vez disso, find -print0 | xargs -0funciona, mas o mesmo acontece
find . -name '*.c' -exec grep 'stdlib.h' {} +
Isso nunca canaliza os nomes de arquivos em qualquer lugar: findagrupa-os em uma grande linha de comando e executa grepdiretamente.
\;em vez de +executar grep separadamente para cada arquivo, o que é muito mais lento. Não faça isso. Mas +é uma extensão do GNU, então você precisa xargsfazer isso com eficiência se não puder assumir a localização do GNU.
Se você deixar de fora xargs, find | grepo padrão será comparado à lista de nomes de arquivos findimpressos.
Então, nesse ponto, é melhor que você faça find -name stdlib.h. Obviamente, com -name '*.c' -name stdlib.h, você não obterá nenhuma saída porque esses padrões não podem corresponder e o comportamento padrão da localização é AND as regras juntas.
Substitua lessem qualquer ponto do processo para ver qual saída qualquer parte do pipeline produz.
Outras leituras: http://mywiki.wooledge.org/BashFAQ tem ótimas coisas.