Como grep string longa em várias linhas, sem saber onde a nova linha é

1

Eu quero procurar uma string específica em várias linhas em um arquivo e obter a linha na qual a correspondência foi encontrada.

No entanto, meu problema é que o arquivo contém uma cadeia de caracteres muito longa, e não palavras (ou seja, palavras), e quero procurar por uma subseqüência dessa longa cadeia de caracteres. Portanto, não posso usar o pcregrep e apenas procurar por word1 \ nword2. Porque eu realmente quero obter o número da linha em que a partida foi encontrada, eu não posso apenas remover todos os caracteres da nova linha ...

Este é um exemplo de como meu arquivo é, apenas capitalizei a string correspondente para que você possa encontrá-la:

String para pesquisar:

gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedcbaedhacebeeebcechbcbfeeccbdhcbfg

Arquivo para pesquisar:

abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
abcdeabcde***GCBCDBFCEEBCFHFCHAACCDGFCEGFFGEDFFAEAEDC
BAEDHACEBEEEBCECHBCBFEECCBDHCBFG***ggfbhbgcedabceedfa
fbaaechaabdbffbebecebaacfcfcdcggfchddcefbcbdegbbba

Alguém de você tem uma solução fácil para isso?

Se não houver uma ferramenta na mão para fazê-lo, eu apenas escreveria um script python curto para fazer isso, mas acho que qualquer ferramenta bash seria mais eficiente do que isso ...

EDITAR :

Muito obrigado pelas suas respostas, elas funcionam muito bem, se a posição do personagem da nova linha for conhecida.

No entanto, sinto muito por ser impreciso na minha pergunta. Meu problema é, que eu não sei se existe uma nova linha, ou mesmo mais de uma nova linha, dentro da string no arquivo e além disso, eu não sei onde é. Eu corrigi minha string de pesquisa excluindo a nova linha que inseri involuntariamente.

Existe alguma maneira de permitir uma nova linha em qualquer posição da string?

    
por TabeaKischka 27.03.2013 / 14:59

3 respostas

1

Estou adicionando uma nova resposta agora que entendi melhor o problema. Estou apenas postando isso como um exemplo de trabalho, mas não afirmo que seja um bom exemplo. :)

Além disso, eu entendo que a pergunta parecia não usar o Python por causa de alguns medos de ineficiência. Então, entendo que essa abordagem não atende a solicitação inteira. : (

#!/usr/bin/env python
import sys

def findall_iter(S, pat):
  index = -1
  while True:
    try:
      index = S.index(pat, index+1)
      yield index
    except ValueError:
      raise StopIteration

def findall(S, pat):
  return list(findall_iter(S, pat))

# read in arguments
S = open(sys.argv[2]).read()
pattern = sys.argv[1]

# get indices of all newlines
newline_indices = findall(S, '\n')

# get psudo-indices of all pattern matches
pat_indices = findall(S.replace('\n', ''), pattern)

# iterate through each pattern match psudo-index and
# correlate it back to a real line number from the file
line_numbers = []
for pi in pat_indices:
  for i, ni in enumerate(newline_indices):
    if ni > pi+i:
      line = i + 1
      if line not in line_numbers:
        line_numbers.append(i+1)
      break

print '\n'.join(map(str, line_numbers))

Prós:

  • Se o arquivo não for muito grande (< 1GB), todas as operações serão realizadas na memória.
  • Usa o método str.index para localizar substrings em vez de correspondência de expressão regular (mais lenta)
  • Mais claro do que usar expressões regulares

Contras:

  • não funciona bem com arquivos grandes.
  • Cria duas strings temporárias para fazer o trabalho.
  • O último for-loop é difícil de entender.
  • É o Python (que eu pessoalmente não acho que seja um engodo).
por 01.04.2013 / 03:26
4

Eu faria isso com um script sed . Coloque isso em um arquivo e use sed -nf para executá-lo.

:restart
/gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc$/{
    #   Found the first part, now discard it
    s/^.*$//
    #   Read a new line into the buffer
    N
    #   Discard the new line inserted by the N operation
    s/^\n//
    #   If next line isn't a match, start over
    /^baedhacebeeebcechbcbfeeccbdhcbfg/!b restart
    #   If it is a match, print the line number
    =
    }

Aqui está o que parece ser executado em bash . Observe que imprime o número da linha da segunda linha correspondida.

bash-4.1$ cat sample.txt
abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
abcdeabcde***gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc
baedhacebeeebcechbcbfeeccbdhcbfg***ggfbhbgcedabceedfa
fbaaechaabdbffbebecebaacfcfcdcggfchddcefbcbdegbbba
bash-4.1$
bash-4.1$ cat findmatch.sed
:restart
/gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc$/{
   #  Found the first part, now discard it
   s/^.*$//
   #  Read a new line into the buffer
   N
   #  Discard the new line inserted by the N operation
   s/^\n//
   #  If next line isn't a match, start over
   /^baedhacebeeebcechbcbfeeccbdhcbfg/!b restart
   #  If it is a match, print the line number
   =
   }
bash-4.1$
bash-4.1$ sed -nf findmatch.sed sample.txt
3
bash-4.1$
    
por 27.03.2013 / 16:15
3

Estou um pouco confuso sobre quais restrições você está operando. No entanto, se você precisar do número da linha, tanto o grep quanto o pcregrep poderão fornecer a você o sinalizador -n.

$ pcregrep -nM "gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc\nbaedhacebeeebcechbcbfeeccbdhcbfg" | cut -d: -f1
2
baedhacebeeebcechbcbfeeccbdhcbfg***ggfbhbgcedabceedfa

pcregrep mostra apenas o número da primeira linha correspondida, aparentemente para que você tenha que pular todas as outras linhas da saída com sed (canalize o acima para sed -n 'p;N' ) se quiser apenas os números de linha como saída. / p>     

por 27.03.2013 / 15:18

Tags