usando xargs para grep multiple patterns

11

Eu tenho um arquivo que tem termos para o qual quero fazer uma busca, com cada termo sendo uma linha no arquivo. Eu estava pensando que poderia fazer isso com xargs. O que eu sou capaz de recolher exemplos da man page como esta

find ./work -print0 | xargs -0 rm

é que xargs acrescenta a saída do comando pre-pipe ao final de seus argumentos. Portanto, se a descoberta retornou report.doc , então o xargs construiria rm report.doc . Esse entendimento está correto?

Então, como quero que os valores em meu arquivo estejam no meio do comando grep, preciso especificar um espaço reservado. Ao brincar, tentei {} , mas não funcionou:

$> cat strings.txt | xargs grep {} subdirectory/*
grep: string1: No such file or directory
grep: string2: No such file or directory

O xargs é a ferramenta certa? Se sim, qual é a sintaxe?

    
por user394 29.02.2012 / 16:00

2 respostas

16

Sim, find ./work -print0 | xargs -0 rm executará algo como rm ./work/a "work/b c" ... . Você pode verificar com echo , find ./work -print0 | xargs -0 echo rm imprimirá o comando que será executado (exceto que o espaço em branco será escapado apropriadamente, embora o echo não mostre isso).

Para obter xargs para colocar os nomes no meio, você precisa adicionar -I[string] , onde [string] é o que você deseja substituir com o argumento. Nesse caso, você usaria -I{} , por exemplo <strings.txt xargs -I{} grep {} directory/* .

O que você realmente quer usar é grep -F -f strings.txt :

-F, --fixed-strings
  Interpret PATTERN as a  list  of  fixed  strings,  separated  by
  newlines,  any  of  which is to be matched.  (-F is specified by
  POSIX.)
-f FILE, --file=FILE
  Obtain  patterns  from  FILE,  one  per  line.   The  empty file
  contains zero patterns, and therefore matches nothing.   (-f  is
  specified by POSIX.)

Portanto, grep -Ff strings.txt subdirectory/* encontrará todas as ocorrências de qualquer sequência em strings.txt como um literal, se você eliminar a opção -F , poderá usar expressões regulares no arquivo. Você pode realmente usar grep -F "$(<strings.txt)" directory/* também. Se você quiser praticar find , poderá usar os dois últimos exemplos no resumo. Se você quiser fazer uma pesquisa recursiva em vez de apenas o primeiro nível, terá algumas opções, também no resumo.

Resumo:

# grep for each string individually.
<strings.txt xargs -I{} grep {} directory/*

# grep once for everything
grep -Ff strings.txt subdirectory/*
grep -F "$(<strings.txt)" directory/*

# Same, using file
find subdirectory -maxdepth 1 -type f -exec grep -Ff strings.txt {} +
find subdirectory -maxdepth 1 -type f -print0 | xargs -0 grep -Ff strings.txt

# Recursively
grep -rFf strings.txt subdirectory
find subdirectory -type f -exec grep -Ff strings.txt {} +
find subdirectory -type f -print0 | xargs -0 grep -Ff strings.txt

Você pode usar a opção -l para obter apenas o nome de cada arquivo correspondente, caso não precise ver a linha real:

-l, --files-with-matches
  Suppress  normal  output;  instead  print the name of each input
  file from which output would normally have  been  printed.   The
  scanning  will  stop  on  the  first match.  (-l is specified by
  POSIX.)
    
por 29.02.2012 / 16:56
4

xargs pode não ser a melhor ferramenta que eu sugiro fgrep se o arquivo de strings-to-match contiver apenas strings, não expressões regulares.

fgrep -f strings.txt subdirectory/*

Sugiro que fgrep como Unix tradicional grep e egrep não tenham a opção "-f". Acredito que o GNU grep e egrep tenham a opção "-f", portanto, se o seu arquivo tiver expressões regulares, você desejará usar uma versão GNU.

    
por 29.02.2012 / 16:58

Tags