Conheço o grep
comando e estou aprendendo sobre as funcionalidades do xargs
, então leio esta página que fornece alguns exemplos de como usar o xargs
comando.
Estou confuso com o último exemplo, exemplo 10. Ele diz "O comando xargs executa o comando grep para encontrar todos os arquivos (entre os arquivos fornecidos pelo comando find) que continham uma string 'stdlib.h'"
$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...
No entanto, qual é a diferença de simplesmente usar
$ find . -name '*.c' | grep 'stdlib.h'
?
Obviamente, ainda estou lutando com o que exatamente xargs está fazendo, então qualquer ajuda é apreciada!
command-line
grep
xargs
Alfa Ômega
fonte
fonte
Respostas:
Isso canaliza a saída (stdout) * de
find
para (stdin of) *grep 'stdlib.h'
como texto (ou seja, os nomes dos arquivos são tratados como texto).grep
faz o que é habitual e encontra as linhas correspondentes neste texto (qualquer nome de arquivo que contenha o padrão). O conteúdo dos arquivos nunca é lido.Isso constrói um comando
grep 'stdlib.h'
para o qual cada resultadofind
é um argumento - portanto, ele procurará correspondências dentro de cada arquivo encontrado porfind
(xargs
pode-se pensar em transformar seu stdin em argumentos para os comandos fornecidos) *Use
-type f
no seu comando find ou você obterá erros nosgrep
diretórios correspondentes. Além disso, se os nomes de arquivos tiverem espaços,xargs
eles estragarão muito, então use o separador nulo adicionando-print0
exargs -0
para obter resultados mais confiáveis:* adicionou esses pontos explicativos extras, como sugerido no comentário por @cat
fonte
|
canaliza stdout para stdin do grep, que não é o mesmo que os argumentos do grep e fornece resultados confusos.find -name '*.c' -exec grep stdlib.h {} +
. Eu praticamente nunca uso xargs. Também fiquei surpreso que ninguém mencionou que o xargs serve a um propósito semelhante paragrep $(find)
comandar a substituição, então escrevi uma resposta minha. Explicar xargs como substituição de comando com menos limitações e problemas parece natural.find -delete
para esse caso especial? Ou para comandos diferentes derm
, se você encontrar o GNU,-exec some_command {} +
agrupe-os em lotes como xargs, em vez disso, o\;
comportamento de executar o comando é separado para cada um.find
executa o comando em cada arquivo se, e somente se, estiver usando-exec command \;
Bothxargs
e-exec command \+
chamará o comando com o número máximo de argumentos permitidos pelo sistema. Em outras palavras, eles são equivalentes #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 comE 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 -0
funciona, mas o mesmo aconteceIsso nunca canaliza os nomes de arquivos em qualquer lugar:
find
agrupa-os em uma grande linha de comando e executagrep
diretamente.\;
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ê precisaxargs
fazer isso com eficiência se não puder assumir a localização do GNU.Se você deixar de fora
xargs
,find | grep
o padrão será comparado à lista de nomes de arquivosfind
impressos.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
less
em qualquer ponto do processo para ver qual saída qualquer parte do pipeline produz.Outras leituras: http://mywiki.wooledge.org/BashFAQ tem ótimas coisas.
fonte
-d
definir o separador, para que você possa-d'\n'
manipular uma lista separada por nova linha, o que pode ser útil se você manipular uma lista de nomes de arquivos em um arquivo, etc. (desde que os nomes dos arquivos não tenham novas linhas em si, que é).myfunc(){ local IFS=$'\n'; fgrep
stdlib.h` $ (find); }
também funciona com o mesmo efeito. Ou, como uma linha, um(IFS=...; cmd...)
subshell também funciona para conter a alteração no IFS sem precisar salvá-lo / restaurá-lo.command $( find )
coisas desse tipo. Nomes de arquivos problemáticos com espaços e caracteres especiais podem quebrar esse tipo de coisa. No mínimo, aspas duplas a substituição do comando.IFS
então é equivalente a usarxargs '-d\n'
. A expansão de globos e o processamento de metacaracteres do shell ocorrem antes dos efeitos da substituição de comandos, então acho que é seguro mesmo com nomes de arquivos que contenham$()
ou>
. Concordou que o uso da divisão de palavras na substituição de comandos não é uma boa prática, exceto para o uso interativo único, onde você sabe algo sobre os nomes de arquivos. Mascommand "$(find)"
só é útil se você espera que ele produzir exatamente 1 filename ...Em geral,
xargs
é usado para casos em que você canaliza (com o símbolo|
) algo de um comando para outro (Command1 | Command2
), mas a saída do primeiro comando não é recebida corretamente como entrada do segundo comando.Isso normalmente acontece quando o segundo comando não manipula a entrada de dados através da Entrada Padrão (stdin) corretamente (por exemplo: Várias linhas como entrada, a forma como as linhas são configuradas, os caracteres usados como entrada, vários parâmetros como entrada, o tipo de dados recebido como entrada, etc.). Para dar um exemplo rápido, teste o seguinte:
Exemplo 1:
ls | echo
- Isso não fará nada, poisecho
não sabe como lidar com a entrada que está recebendo. Agora, neste caso, se a usarmos,xargs
ela processará a entrada de uma maneira que possa ser manipulada corretamente porecho
( por exemplo: Como uma única linha de informação)ls | xargs echo
- Isso produzirá todas as informaçõesls
em uma única linhaExemplo 2:
Digamos que eu tenha vários arquivos goLang dentro de uma pasta chamada go. Eu procuraria por eles com algo assim:
find go -name *.go -type f | echo
- Mas se o símbolo do cano ali eecho
no final não funcionasse.find go -name *.go -type f | xargs echo
- Aqui funcionaria graças a,xargs
mas se eu quisesse cada resposta dofind
comando em uma única linha, faria o seguinte:find go -name *.go -type f | xargs -0 echo
- Nesse caso, a mesma saída defind
seria mostrada porecho
.Comandos como
cp, echo, rm, less
e outros que precisam de uma maneira melhor de lidar com a entrada são beneficiados quando usadosxargs
.fonte
xargs
é usado para gerar automaticamente argumentos de linha de comando com base (geralmente) em uma lista de arquivos.Portanto, considerando algumas alternativas ao uso do
xargs
comando a seguir :Há vários motivos para usá-lo em vez de outras opções que não foram mencionadas originalmente em outras respostas:
find . -name '*.c' -exec grep 'stdlib.h' {}\;
irá gerar umgrep
processo para cada arquivo - isso geralmente é considerado uma prática ruim e pode sobrecarregar o sistema se houver muitos arquivos encontrados.grep 'stdlib.h' $(find . -name '*.c')
comando provavelmente falhará, porque a saída da$(...)
operação excederá o comprimento máximo da linha de comando do shellConforme mencionado em outras respostas, a razão para usar o
-print0
argumentofind
neste cenário e o-0
xargs é que nomes de arquivos com certos caracteres (por exemplo, aspas, espaços ou até novas linhas) ainda sejam manipulados corretamente.fonte