grep'ping arquivos para várias cadeias de caracteres (não na mesma linha)

6

Mais frequentemente, gosto de admitir que procuro por um ficheiro que contenha algumas cadeias de caracteres.

Atualmente eu faço:

grep -rl string1 | xargs grep -l string2 | xargs grep -l string3

Existe uma ferramenta que faz isso mais bonita?

Este grep s para string1 ou string2 ou string3 .

grep -rl -e string1 -e string2 -e string3

Eu quero arquivos que contenham string1 e string2 e string3 , mas não necessariamente na mesma linha.

Talvez um dos modernos grep s ( ag/ack/rg/sift ) possa fazer isso?

    
por Ole Tange 31.08.2018 / 20:52

4 respostas

6

Você pode usar grep desta maneira:

grep -rzlP '(?s)(?=.*?string1)(?=.*?string2)(?=.*?string3)' .
  • (?s) Conhecido "dot-all", que diz ao grep que permite que o ponto . corresponda também a \n caracteres do ewline. '

  • (?=.*?pattern) : Lookahead positivo, que corresponde a todos os caracteres . que ocorreram zero ou mais vezes * e não-ganancioso seguido por um padrão ( string1 , string2 , ...) .

Você pode fazer uma função como a seguir (POSIX bash & zsh ):

mgrep() { eval grep -rzlP $(printf ''\''(?s)';
          printf '(?=.*?'\''"$%d"'\'')' $(eval echo {1..$#}); printf ''\''') . ; }

depois, chame como abaixo e ele procurará os arquivos recursivamente dentro do diretório de trabalho atual com todos os padrões em

.
mgrep string1 string2 string3

ele também irá lidar com todos os tipos de padrões, pois o grep suporta ( ajusta a opção grep na função de acordo com sua necessidade com antecedência ).

mgrep string 'pattern with space' '\d+' [0-9]  [...]
    
por 31.08.2018 / 21:30
4

Com agrep (o % aproximado grep , não o de tre ), você pode fazer

agrep -ld '$x' 'pattern1;pattern2;pattern3'

Onde usamos o regexp que não pode corresponder ( $x , algo após o final) como o delimitador.

(use find ou zsh recursive globs para pesquisar em todos os arquivos em um diretório recursivamente).

Embora os padrões sejam comparados com todo o conteúdo dos arquivos, não cada linha de cada arquivo.

Você pode criar scripts com o gawk com:

PATTERNS='pattern1;pattern2;pattern3' gawk -e '
  BEGIN{n = split(ENVIRON["PATTERNS"], a, ";")}
  BEGINFILE{for (i in a) p[a[i]]; found = 0}
  {
    for (i in p)
      if ($0 ~ i) {
        if (++found == n) {print FILENAME; nextfile}
        delete p[i]
      }
  }' -E /dev/null file1 file2...

(embora seja bem lento).

    
por 01.09.2018 / 09:37
1

Com base na resposta de αғsнιη:

mgrep() {
    grep -rzlP "(?s)$(printf "(?=.*?%s)" "$@")" .
}

mgrep string1 string2 string3
    
por 02.09.2018 / 10:30
0

A seguinte proposta é simples, mas provavelmente poderia ser mais eficiente e robusta.

#!/bin/bash

tab=(one three five)

# grep_all's return status indicates if all patterns have at least
# one matching result in the text file specified as argument.

grep_all()
{
    local -n patterns=$1      # allows to refer to an array
    local file=$2

    # abort if a pattern is not found
    for pattern in "${patterns[@]}"; do
        if ! grep -q -e "$pattern" "$file"; then
            return 1
        fi
    done
}

grep_all tab file.txt
echo $?
    
por 01.09.2018 / 09:25