Pesquisando correspondência de regex multilinha em arquivos (sem pcregrep)

0

Pergunta:

Como posso encontrar correspondências de expressões regulares multilinhas em arquivos, sem pcregrep?

Eu preciso encontrar / imprimir a posição de cada ocorrência.

Infelizmente, o pcregrep não está presente e não tenho direitos para instalá-lo. Outras alternativas são grep perl sed python etc.

Um exemplo de expressão regular para pesquisa é:

Text\nLine

Contexto:

Um script fornece centenas de MB de texto estruturado em algumas dezenas de arquivos, mas infelizmente algumas linhas estão faltando (devido a vários motivos). Eu preciso verificar onde essas linhas estão faltando, pesquisando assim a sequência das linhas anteriores e seguintes.

Text
Missing //this line is sometimes missing.
Line

EDITADO:

Possível entrada

example.txt

Text
Missing
Line

Text
Missing
Line

Text
Line

Text
Missing
Line

Saída possível:

example.txt, line 10

Algumas das tentativas sem sucesso:

pcregrep 
    # command not found
apt-get install pcregrep 
    # no permission, no su credentials, distro don't provide pcregrep, outdated sources, customer does not want changes on the serve, etc.
sed -r 's#(Text\nLine)##' ./* 
    # print all lines, not only matches, no indication of file or line, etc.
grep 'Text\nLine' ./* 
    # Does not works on multi-lines
sed -n '/Text/,/Line/{p}' ./* 
    # Not the same regex, does not indicate result lines, etc.
    
por Adrian Maire 11.06.2018 / 09:41

3 respostas

2

As ferramentas Unix são mais frequentemente orientadas a linhas e, portanto, não há como aplicar uma expressão regular em várias linhas de entrada usando a caixa de ferramentas padrão.

sed pode ser feito para processar o arquivo de forma que ele consiga detectar as linhas que você está procurando, mas fazemos isso estritamente usando operações em linhas individuais:

$ sed -n '/^Text/{N;/^Text\nLine/=;D;}' file
10

Este script sed procura a string Text no início de uma linha. Quando encontrado, ele anexa a próxima linha ao seu buffer com um \n intermediário.

Se o buffer agora corresponder a ^Text\nLine , o número da linha atual será gerado usando o comando = em sed . O número da linha resultante é o da linha Line no arquivo.

Observe que, embora a segunda expressão regular pareça corresponder a uma nova linha no arquivo, isso não acontece. Ele corresponde a uma nova linha em seu buffer interno, que nós colocamos lá usando o comando N quando lemos a próxima linha do arquivo.

Você provavelmente usaria isso em um loop se quiser aplicá-lo em vários arquivos:

for name in pattern; do
    printf 'Processing %s...\n' "$name"
    sed -n '/^Text/{N;/^Text\nLine/=;D;}' "$name"
done

onde pattern seria um padrão de globbing de nome de arquivo comum que corresponde aos arquivos em que você está interessado.

    
por 11.06.2018 / 10:49
1

Se vim estiver instalado, você poderá usá-lo no modo ex como:

vim -e -s -c 'argdo g/^Text\nLine/#' -c q ./*.txt

Veja também o comando z para fornecer contexto.

vim -e -s -c 'argdo g/^Text\nLine/z#.5' -c q ./*.txt

Isso não imprime os nomes dos arquivos. Uma abordagem não muito eficiente de perl poderia ser:

perl -l -0777 -ne 'while (/Text\nLine/g) {
   print "$ARGV, line " . ++(() = $' =~ /\n/g)}' ./*.txt
    
por 11.06.2018 / 11:49
0
 perl -ne 'eof and $. = 0 or /^Text/ && ($_ .= <>) =~ /^Line/m && print "$ARGV: $.\n"' ./*

Isso imprimirá o nome do arquivo junto com o número da linha onde ocorreu a correspondência.

Além disso, o contador de linha ($.) é redefinido ao atingir eof de cada arquivo.

    
por 12.06.2018 / 02:01