Como usar> em um comando xargs?

160

Eu quero encontrar um comando bash que me permita grep todos os arquivos em um diretório e escreva a saída desse grep em um arquivo separado. Meu palpite teria sido fazer algo assim

ls -1 | xargs -I{} "grep ABC '{}' > '{}'.out"

mas, tanto quanto eu sei, xargs não gosta de aspas duplas. Se eu remover as aspas duplas, no entanto, o comando redirecionará a saída do comando inteiro para um único arquivo chamado '{}'. Out em vez de para uma série de arquivos individuais.

Alguém sabe de uma maneira de fazer isso usando xargs? Eu apenas usei esse cenário grep como um exemplo para ilustrar meu problema com o xargs, para que quaisquer soluções que não usem o xargs não sejam tão aplicáveis ​​para mim.

Jesse Shieh
fonte

Respostas:

201

Não cometa o erro de fazer isso:

sh -c "grep ABC {} > {}.out"

Isso ocorrerá sob várias condições, incluindo nomes de arquivos descolados e é impossível citar corretamente. Você {}deve sempre ser um único argumento completamente separado para o comando para evitar erros de injeção de código. O que você precisa fazer é o seguinte:

xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}

Aplica-se a xargse também find.

A propósito, nunca use xargs sem a -0opção (a menos que seja para uso interativo único e muito raro e controlado, onde você não está preocupado em destruir seus dados).

Também não analise ls. Sempre. Use globbing ou, em findvez disso:http://mywiki.wooledge.org/ParsingLs

Use findpara tudo o que precisa de recursão e um loop simples com um glob para todo o resto:

find /foo -exec sh -c 'grep "$1" > "$1.out"' -- {} \;

ou não recursivo:

for file in *; do grep "$file" > "$file.out"; done

Observe o uso adequado de aspas.

lhunath
fonte
Voto a favor, mas com uma dúvida lamentável. não usar xargssem -0: isso só se aplica quando você canalizafind a saída xargs, certo? quando eu faço, xargs -a <input_file>como eu usaria isso? A maioria dos comandos, como grepsaídas com \ne não, é \0.a única maneira de solucionar esse problema, usando-o trnovamente para corrigir isso, talvez. Mas por que é importante usá-lo apenas com -0?
legends2k
3
@ legends2k porque quando você não usa -0, xargspega seus nomes de arquivos e quebra todos os espaços, aspas e barras invertidas neles. Você deve apenas esquecer xargscomo uma ferramenta. Se você tiver linhas, use um loop bash para iterar as linhas while read line; do <command> "$REPLY"; done < file-with-linescommand | while ...
:,
1
Uau, não sabia disso, obrigado pelos detalhes! Portanto, para portabilidade (já que nem todos xargssão GNU), xargsprecisa ser evitado, a menos que se possa usá-lo -0. Obrigado.
legends2k
1
Embora eu aprecie a explicação detalhada para este caso de uso específico, a questão é sobre o redirecionamento da saída de xargs , que nem sempre envolve análise lsou uso sh -c. Isso não responde à pergunta nem um pouco, mas é o primeiro resultado do Google para a pergunta, apenas aumentando a confusão.
Pandasauce
1
@Ihunath, Oi, sua resposta funciona bem para mim. Mas você poderia dar uma explicação detalhada ou links sobre xargs -I{} sh -c 'grep ABC "$1" > "$1.out"' -- {}? Especialmente, as regras de aspas incorporadas (duplas) e o símbolo "-" no final. Obrigado
Scott Yang
40

Uma solução sem xargsé a seguinte:

find . -mindepth 1 -maxdepth 1 -type f -exec sh -c "grep ABC '{}' > '{}.out'" \;

... e o mesmo pode ser feito com xargs :

ls -1 | xargs -I {} sh -c "grep ABC '{}' > '{}.out'"

Edit : aspas simples adicionadas após observação por lhunath .

Stephan202
fonte
Ele disse que quer usar xargs. Também postei uma solução sem ela, mas excluí uma vez que vi que ele precisava de xargs.
Zifre 10/05/09
Você está certo. A razão pela qual publiquei minha resposta foi que é melhor ter uma solução alternativa para fazer o trabalho do que nenhuma. Acontece que ele me colocou no caminho certo para encontrar a resposta desejada (ou seja, o truque sh -c).
2020 Stephan202
14

Suponho que seu exemplo seja apenas um exemplo e que você pode precisar de outras coisas. O GNU Parallel http://www.gnu.org/software/parallel/ pode ser o seu resgate. Ele não precisa de citação adicional, contanto que seus nomes de arquivos não contenham \ n:

ls | parallel "grep ABC {} > {}.out"

Se você tiver nomes de arquivos com \ n:

find . -print0 | parallel -0 "grep ABC {} > {}.out"

Como um bônus adicional, você executa os trabalhos em paralelo.

Assista aos vídeos de introdução para saber mais: http://pi.dk/1

A instalação de 10 segundos tentará fazer uma instalação completa; se isso falhar, uma instalação pessoal; se isso falhar, uma instalação mínima:

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 3374ec53bacb199b245af2dda86df6c9
12345678 3374ec53 bacb199b 245af2dd a86df6c9
$ md5sum install.sh | grep 029a9ac06e8b5bc6052eac57b2c3c9ca
029a9ac0 6e8b5bc6 052eac57 b2c3c9ca
$ sha512sum install.sh | grep f517006d9897747bed8a4694b1acba1b
40f53af6 9e20dae5 713ba06c f517006d 9897747b ed8a4694 b1acba1b 1464beb4
60055629 3f2356f3 3e9c4e3c 76e3f3af a9db4b32 bd33322b 975696fc e6b23cfb
$ bash install.sh

Se você precisar movê-lo para um servidor que não possua o GNU Parallel instalado, tente parallel --embed.

Ole Tange
fonte