Como executo xargs grep na saída grep que possui espaços?

8

Estou pesquisando arquivos com base em uma expressão regular e, em seguida, tentando pesquisar conteúdo nesses arquivos. Então, por exemplo, eu tenho algo como

#Find all C++ files that match a certain pattern and then search them
find . -name "*.cpp" | grep "<name regex>" | xargs grep "<content regex>"

O problema que estou enfrentando é que alguns dos caminhos têm espaços neles, o que confunde xargs. Eu sei que, se eu estivesse usando find, poderia usar o -print0argumento (junto com o -0argumento xargs) para impedir que os xargs tratem os espaços como delimitadores. Existe algo semelhante com grep?

Ou estou abordando esse problema de maneira totalmente errada? Ingenuamente, findque grepa xargs grepfaz sentido para mim, mas estou aberto a outras abordagens que produzem os mesmos resultados.

quanticle
fonte
2
você pode posicionar argumentos xargsusando o -iparâmetro à la cat sample.txt | grep "pat t ern" | xargs -i grep "{}"- os chavetas indicam onde posicionar o argumento. O manual me diz que isso -ifoi preterido em favor, -Ientão talvez valha a pena dar uma olhada nisso também.
DougBTV

Respostas:

5

Use algo assim, talvez (se gnu grep).

grep -r 'content pattern' --include==*.cpp

homem grep

--include = GLOB Pesquise apenas arquivos cujo nome base corresponda ao GLOB (usando a correspondência curinga conforme descrito em --exclude)

Veja também as opções para delimitadores nulos.

-Z, --null Gera um byte zero (o caractere ASCII NUL) em vez do caractere que normalmente segue um nome de arquivo. Por exemplo, grep -lZ gera um byte zero após cada nome de arquivo, em vez da nova linha usual. Essa opção torna a saída inequívoca, mesmo na presença de nomes de arquivos contendo caracteres incomuns, como novas linhas. Essa opção pode ser usada com comandos como find -print0, perl -0, sort -z e xargs -0 para processar nomes de arquivos arbitrários, mesmo aqueles que contêm caracteres de nova linha.

-z, --null-data Trate a entrada como um conjunto de linhas, cada uma terminada por um byte zero (o caractere ASCII NUL) em vez de uma nova linha. Como a opção -Z ou --null, essa opção pode ser usada com comandos como sort -z para processar nomes de arquivos arbitrários.

Zoredache
fonte
Observe que grep -r include='*.cpp'é um shell glob - e o mesmo acontece com o recurso alinhado w / find . -name '*.cpp' -exec grep -e 'content_pattern' -- {} \;not w /find . -name '*.cpp' | grep 'name_pattern' | xargs grep 'content_pattern'
mikeserv
4

Se você precisar pular muitos obstáculos, a eficiência do xargs será perdida de qualquer maneira. Aqui está um trabalho bruto:

find . -iname "*.cpp" | grep "<pattern>" | while read -r x; do grep exa "$x"; done

Sempre que encontro problemas com espaços nos nomes de arquivos, a resposta é aspas duplas em uma variável.

Baazigar
fonte
Isso executa o grep interno do loop exclusivamente para cada linha encontrada pelo grep externo. Isso é muita sobrecarga.
Adam Katz
3

Use findpara fazer toda a filtragem de nome de arquivo. Ao invés de

find . -name "*.cpp" | grep "foo" | xargs grep 

Faz

find . -name "*.cpp" -name "*foo*" -print0 | xargs -0 grep 

Se você quiser fazer algo um pouco mais complicado, como

find . -name "*.cpp" | egrep "foo|bar" | xargs grep 

você pode fazer

find . -name "*.cpp" "(" -name "*foo*" -o -name "*bar*" ")" -print0 | xargs -0 grep 

Observe que eles devem funcionar mesmo para arquivos com novas linhas em seus nomes.

E, se você precisar do poder de expressões regulares completas, poderá usar -regex.

Scott
fonte
2

Isso deve funcionar mesmo sem as ferramentas GNU:

#Find all C++ files that match a certain pattern and then search them
find . -name "*.cpp"  | grep "<name regex>" | perl -pe 's/\n/\0/' \
  | xargs -0 grep "<content regex>"

A perlchamada substitui quebras de linha por caracteres nulos, o que permitirá xargs -0interpretar a entrada por linha e não por espaço em branco.

Usando o GNU, você pode remover a perlchamada e mudar xargs -0 …paraxargs -d "\n" …

Não tem perlou GNU? Tente em awk '{printf "%s%c", $0, 0}'vez disso.

Adam Katz
fonte
1
Isso pode não fazer a coisa certa se alguns dos nomes de arquivos incluírem novas linhas (uma ocorrência bastante incomum, com certeza, mas não impossível).
dhag
@dhag tem um ponto válido a respeito xargs -d "\n". Essa é uma ocorrência muito incomum, mas se você não tiver controle dos dados e estiver preocupado com o risco de segurança, tenha cuidado com as expectativas de saída.
Adam Katz