usando xargs para grep múltiplos padrões

12

Eu tenho um arquivo com os termos que eu quero saudar, com cada termo sendo uma linha no arquivo. Eu estava pensando que poderia fazer isso com xargs. O que eu consigo captar de exemplos da página de manual como este

find ./work -print0 | xargs -0 rm

é que xargs anexa a saída do comando pre-pipe ao final de seus argumentos. Portanto, se a descoberta retornasse report.doc, o xargs seria construído rm report.doc. Esse entendimento está correto?

Portanto, como eu quero que os valores no 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

Xargs é a ferramenta certa? Se sim, qual é a sintaxe?

user394
fonte

Respostas:

17

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

Para xargscolocar os nomes no meio, é necessário adicionar -I[string]onde [string]é o que você deseja substituir pelo argumento; nesse caso, você usaria -I{}, por exemplo <strings.txt xargs -I{} grep {} directory/*.

O que você realmente deseja 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 strings.txtcomo literal, se você soltar a -Fopção, poderá usar expressões regulares no arquivo. Você também pode usar grep -F "$(<strings.txt)" directory/*. Se você deseja praticar find, pode usar os dois últimos exemplos no resumo. Se você deseja fazer uma pesquisa recursiva em vez do primeiro nível, você tem 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 -lopção para obter apenas o nome de cada arquivo correspondente, se não precisar 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.)
Kevin
fonte
4

xargspode não ser a melhor ferramenta que eu sugeriria fgrepse seu arquivo de cadeias de caracteres contiver apenas cadeias de caracteres, não expressões regulares.

fgrep -f strings.txt subdirectory/*

Eu sugiro fgrepcomo Unix tradicional grepe egrepnão tinha a opção "-f". Acredito que o GNU grepe egreptenha a opção "-f", portanto, se seu arquivo tiver expressões regulares, você desejaria usar uma versão do GNU.

Bruce Ediger
fonte