Como você constrói um grep & command para combinar com um número de padrões dependendo de quantos são fornecidos no tempo de execução?

2

Eu exportei alguns documentos MS Word para texto simples e use esta função para analisar o conteúdo dos arquivos .txt no diretório atual:

mo1 () {
for i in *.txt; do
    echo "File: $i"
    grep -n -HC 1 "$@" "$i"
done
}

Se eu tivesse mais de um padrão para procurar, poderia fazer mo1 | grep pattern2 . Mas e se eu quiser fazer algo cujo resultado seria como grep -E 'pattern1.*pattern2[.*...]...' dependendo de quantos padrões são fornecidos para a função em tempo de execução, por exemplo, mo1 pattern1 pattern2 [...] etc.? Eu posso ver o @ array poderia fornecer o número de itens e eu poderia construir através de um loop uma variável (finalpattern = '$ 1. * $ 2. * $ 3') que acabaria sendo a expressão usada para grep . Mas não consigo pensar em como abstrair esse bit onde você cria a expressão na função? Ou existe uma maneira melhor / mais simples de fazer algo assim?

    
por jus cogens prime 18.07.2014 / 00:47

2 respostas

4

Você pode aproveitar o printf incorporado.

mo1 () {
  for file in *.txt; do
    grep -n -C1 "$(printf "%s.*" "$@")" "$file"
  done
}

Esta versão simples insere .* após o último elemento. Não importa para este caso de uso específico, mas em outros casos (por exemplo, grep -o ) você pode precisar retirar o .* extra no final.

mo1 () {
  pattern=$(printf "%s.*" "$@")
  pattern=${pattern%??}
  for file in *.txt; do
    grep -n -C1 "$pattern" "$file"
  done
}

No bash, você pode colocar a saída printf diretamente em uma variável, o que é um pouco mais rápido do que usar uma substituição de comando (mas é improvável que isso seja importante, mesmo no Cygwin onde as subshells são lentas).

mo1 () {
  printf -v pattern "%s.*" "$@"
  pattern=${pattern%??}
  for file in *.txt; do
    grep -n -C1 "$pattern" "$file"
  done
}

Se você quisesse inserir um único caractere entre os parâmetros posicionais, poderia definir IFS para esse caractere e usar "$@" . Mas isso não funciona se o separador tiver mais de um caractere. Em ksh e bash, se houver um caractere que não apareça no padrão, você poderá usá-lo para unir-se e, em seguida, realizar uma substituição. Por exemplo, aqui, não faria sentido que os padrões contivessem novas linhas, então:

mo1 () {
  typeset IFS=$'\n'
  typeset pattern="$*"
  pattern=${pattern//$'\n'/.*}
  for file in *.txt; do
    grep -n -C1 "$pattern" "$file"
  done
}

No zsh, claro, há um caminho direto.

mo1 () {
  for file in *.txt; do
    grep -n -C1 ${(j:.*:)@} $file
  done
}
    
por 18.07.2014 / 01:06
0

Como alternativa, você pode usar a opção --file para grep:

-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.)
    
por 18.07.2014 / 06:01