Como encontrar um arquivo usando vários padrões

2

Gostaria de encontrar um arquivo usando vários padrões.

Este é o meu comando original: mas é longo para digitar e xargs zgrep está sendo redundante. Imagine se eu tivesse 10 ou mais padrões para inserir?

find -mtime -$a -type f ! -name "*.bak*" | xargs zgrep -il "$b" | xargs zgrep -il "$c" | xargs zgrep -il "$d" | xargs zgrep -il 'ST.997' | sort -u

Eu queria menos caracteres para digitar, por exemplo:

find -mtime -$a -type f ! -name "*.bak*" | xargs zgrep -il "$b && $c && $d" | sort -u

EDITAR: Se você perceber que os padrões estão associados a $ . Isso porque o comando está dentro de um script e essas variáveis têm valores string / numéricos.

Vou usar isso para melhorar meu script, especialmente seu tempo de execução.

    
por WashichawbachaW 24.08.2017 / 09:57

3 respostas

5

Se você quiser evitar a descompactação do arquivo várias vezes para cada padrão, faça o seguinte:

PATTERNS='foo
bar
baz' find . -mtime -"$a" -type f ! -name "*.bak*" -exec awk -v q=\' '
  function shquote(s) {
    gsub(q, q "\" q q, s)
    return q s q
  }
  BEGIN {
    n = split(ENVIRON["PATTERNS"], pats, "\n")
    for (arg = 1; arg < ARGC; arg++) {
      file = ARGV[arg]
      cmd = "gzip -dcf < " shquote(file)
      for (i = 1; i <= n; i++) notfound[pats[i]]
      left = n
      while (left && (cmd | getline line) > 0) {
        for (pat in notfound) {
          if (line ~ pat) {
            if (!--left) {
              print file
              break
            }
            delete notfound[pat]
          }
        }
      }
      close(cmd)
    }
    exit
  }' {} +

Observe que os padrões são tomados como padrões awk , que são semelhantes às expressões regulares estendidas suportadas por grep -E / egrep . Para correspondência insensível a maiúsculas e minúsculas, você pode adicionar um -v IGNORECASE=1 se estiver usando o GNU awk ou alterar portavelmente para:

PATTERNS='foo
bar
baz' find . -mtime -"$a" -type f ! -name "*.bak*" -exec awk -v q=\' '
  function shquote(s) {
    gsub(q, q "\" q q, s)
    return q s q
  }
  BEGIN {
    n = split(tolower(ENVIRON["PATTERNS"]), pats, "\n")
    for (arg = 1; arg < ARGC; arg++) {
      file = ARGV[arg]
      cmd = "gzip -dcf < " shquote(file)
      for (i = 1; i <= n; i++) notfound[pats[i]]
      left = n
      while (left && (cmd | getline line) > 0) {
        line = tolower(line)
        for (pat in notfound) {
          if (line ~ pat) {
            if (!--left) {
              print file
              break
            }
            delete notfound[pat]
          }
        }
      }
      close(cmd)
    }
    exit
  }' {} +

(assumindo que os padrões não possuem extensões ERE não padrão, como \S , que seriam convertidos em \s ).

Você pode colocar esse comando awk em um script zgrep-many para facilitar o uso. Algo como:

#! /bin/sh -

usage() {
  cat >&2 << EOF
Usage: $0 [-e <pattern>] [-f <file] [-i] [pattern] files

List the files for which all the given patterns are matched.
EOF
  exit 1
}

ignorecase= 
PATTERNS=
export PATTERNS
NL='
'
sep=

while getopts e:f:i opt; do
  case $opt in
    (e) PATTERNS=$PATTERNS$sep$OPTARG; sep=$NL;;
    (f) PATTERNS=$PATTERNS$sep$(cat < "$OPTARG") || exit; sep=$NL;;
    (i) ignorecase='tolower(';;
    (*) usage;;
  esac
done
shift "$((OPTIND - 1))"
if [ -z "$PATTERNS" ]; then
  [ "$#" -gt 0 ] || usage
  PATTERN=$1; shift
fi

[ "$#" -eq 0 ] && exit

exec awk -v q=\' '
  function shquote(s) {
    gsub(q, q "\" q q, s)
    return q s q
  }
  BEGIN {
    n = split('"$ignorecase"'ENVIRON["PATTERNS"]'"${ignorecase:+)}"', pats, "\n")
    for (arg = 1; arg < ARGC; arg++) {
      file = ARGV[arg]
      cmd = "gzip -dcf < " shquote(file)
      for (i = 1; i <= n; i++) notfound[pats[i]]
      left = n
      while (left && (cmd | getline line) > 0) {
        '"${ignorecase:+line = tolower(line)}"'
        for (pat in notfound) {
          if (line ~ pat) {
            if (!--left) {
              print file
              break
            }
            delete notfound[pat]
          }
        }
      }
      close(cmd)
    }
    exit
  }' "$@"

Para ser usado como:

find ... -exec zgrep-many -ie foo -e bar -e baz {} +

por exemplo.

    
por 24.08.2017 / 14:07
2

grep não possui a opção AND para corresponder a vários padrões, mas você pode essencialmente OU combinar padrões usando | . Se você usar a sintaxe estendida, poderá combinar vários padrões com todas as combinações deles:

a.*b.*c|a.*c.*b|b.*a.*c|b.*c.*a|c.*a.*b|c.*b.*a

Mas provavelmente não é uma boa idéia se você tiver mais de dois padrões, pois o número de combinações aumenta rapidamente.

Você pode combinar seus comandos zgrep usando -exec . Use a opção silenciosa -q para cada zgrep , exceto a última (que imprime o nome do arquivo se ele e todos os greps anteriores encontraram uma correspondência).

find -mtime -$a -type f ! -name "*.bak*"      \
        -exec zgrep -iq "$b" {} \;            \
        -exec zgrep -iq "$c" {} \;            \
        -exec zgrep -il "$d" {} \; | sort
    
por 24.08.2017 / 10:33
1

Você pode usar find com um comando executando três zgrep -s como

  find -mtime -$a -type f ! -name "*.bak*"      \
       -exec zgrep -q {} "$b" \; \
       -a   -exec zgrep -q {} "$c" \; \
       -a   -exec zgrep -q {} "$d" \; \
    | sort

Você também pode coletar primeiro os nomes dos arquivos em grep , por exemplo,

 find -mtime -$a -type f ! -name "*.bak*" > /tmp/file-list

(assumindo que seus nomes de arquivos são bons, sem espaços)

faça um loop em todas as linhas em /tmp/file-list

Por fim, você poderia escrever um script em outro idioma ( awk , Python, ...)

e, para evitar a digitação, você pode definir uma função de shell.

    
por 24.08.2017 / 11:31