Parece que eu estou fazendo mau uso grep
/ egrep
.
Eu estava tentando procurar seqüências de caracteres em várias linhas e não consegui encontrar uma correspondência enquanto sei que o que estou procurando deve corresponder. Originalmente, pensei que minhas expressões regulares estavam erradas, mas acabei lendo que essas ferramentas operam por linha (também minhas expressões regulares eram tão triviais que não poderiam ser o problema).
Então, qual ferramenta seria usada para pesquisar padrões em várias linhas?
grep
. Eles estão intimamente relacionados, mas não são bobos, IMO."grep"
sugerindo o verbo "to grep" e as principais respostas, incluindo aceitas, não usam grep.Respostas:
A seguir, apresentamos um comportamento semelhante a
sed
vocêgrep
em várias linhas:Como funciona
-n
suprime o comportamento padrão de imprimir todas as linhas/foo/{}
instrui-o a combinarfoo
e fazer o que vem dentro dos rabiscos para as linhas correspondentes. Substituirfoo
pela parte inicial do padrão.:start
é um rótulo de ramificação para nos ajudar a continuar em loop até encontrarmos o fim de nossa regex./bar/!{}
executará o que está nos squigglies nas linhas que não correspondembar
. Substituirbar
pela parte final do padrão.N
anexa a próxima linha ao buffer ativo (sed
chama isso de espaço padrão)b start
incondicionalmente ramificará para ostart
rótulo que criamos anteriormente, para continuar anexando a próxima linha, desde que o espaço do padrão não contenhabar
./your_regex/p
imprime o espaço do padrão, se corresponderyour_regex
. Você deve substituiryour_regex
por toda a expressão que deseja corresponder em várias linhas.fonte
sed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
sed: unterminated {
errosed
implementações. Tentei seguir as recomendações nessa resposta para tornar o script acima compatível com os padrões, mas ele me disse que "start" era um rótulo indefinido. Portanto, não tenho certeza se isso pode ser feito de maneira compatível com os padrões. Se você o gerencia, sinta-se à vontade para editar minha resposta.Eu geralmente uso uma ferramenta chamada
pcregrep
que pode ser instalada na maior parte do sabor do Linux usandoyum
orapt
.Por exemplo.
Suponha que você tenha um arquivo nomeado
testfile
com conteúdoVocê pode executar o seguinte comando:
para fazer a correspondência de padrões em várias linhas.
Além disso, você também pode fazer o mesmo
sed
.fonte
Aqui está uma abordagem mais simples usando o Perl:
ou (desde que JosephR seguiu o
sed
caminho , roubarei descaradamente sua sugestão )Explicação
$f=join("",<>);
: Este lê o arquivo inteiro e salva o seu conteúdo (novas linhas e tudo) para a variável$f
. Em seguida, tentamos corresponderfoo\nbar.*\n
e imprimi-lo se corresponder (a variável especial$&
mantém a última correspondência encontrada). O///m
é necessário para fazer a correspondência de expressão regular em toda a novas linhas.A
-0
define o separador de registro de entrada. Definir isso para00
ativar o 'modo de parágrafo' onde o Perl usará novas linhas consecutivas (\n\n
) como separador de registros. Nos casos em que não há novas linhas consecutivas, o arquivo inteiro é lido (descartado) de uma só vez.Atenção:
Você não fazer isso para arquivos grandes, ele vai carregar o arquivo inteiro na memória e que pode ser um problema.
fonte
Uma maneira de fazer isso é com o Perl. por exemplo, aqui está o conteúdo de um arquivo chamado
foo
:Agora, aqui estão alguns Perl que correspondem a qualquer linha que comece com foo, seguida por qualquer linha que comece com bar:
O Perl, dividido:
while(<>){$all .= $_}
Isso carrega toda a entrada padrão na variável$all
while($all =~
Enquanto a variávelall
tem a expressão regular .../^(foo[^\n]*\nbar[^\n]*\n)/m
O regex: foo no início da linha, seguido por qualquer número de caracteres que não sejam de nova linha, seguido por uma nova linha, seguida imediatamente por "bar" e o restante da linha com barra./m
no final da regex significa "correspondência entre várias linhas"print $1
Imprima a parte da regex que estava entre parênteses (nesse caso, toda a expressão regular)s/^(foo[^\n]*\nbar[^\n]*\n)//m
Apague a primeira correspondência para a regex, para que possamos corresponder a vários casos da regex no arquivo em questãoE a saída:
fonte
perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
A alternativa grep Sift suporta correspondência de várias linhas (disclaimer: Eu sou o autor).
Suponha que
testfile
contenha:sift -m '<description>.*?</description>'
(mostre as linhas que contêm a descrição)Resultado:
sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename
(extrair e reformatar a descrição)Resultado:
fonte
Simplesmente um grep normal que suporta
Perl-regexp
parâmetrosP
fará esse trabalho.(?s)
chamado modificador DOTALL, que faz o ponto no seu regex corresponder não apenas aos caracteres, mas também às quebras de linha.fonte
-P
opçãoEu resolvi este aqui para mim usando grep e -A opção com outro grep.
A opção -A 1 imprime 1 linha após a linha encontrada. Obviamente, depende da sua combinação de arquivos e palavras. Mas, para mim, era a solução mais rápida e confiável.
fonte
Suponha que tenhamos o arquivo test.txt contendo:
O código a seguir pode ser usado:
Para a seguinte saída:
fonte
Se quisermos colocar o texto entre os 2 padrões, excluindo-se.
Suponha que tenhamos o arquivo test.txt contendo:
O código a seguir pode ser usado:
Para a seguinte saída:
Como funciona, vamos fazer passo a passo
/foo/{
é acionado quando a linha contém "foo"n
substitua o espaço do padrão pela próxima linha, ou seja, a palavra "aqui"b gotoloop
ramo para o rótulo "gotoloop":gotoloop
define o rótulo "gotoloop"/bar/!{
se o padrão não contiver "barra"h
substitua o espaço de espera pelo padrão, para que "aqui" seja salvo no espaço de esperab loop
ramificar para o rótulo "loop":loop
define o rótulo "loop"N
anexa o padrão ao espaço de espera.Agora, o espaço de espera contém:
"aqui"
"é o"
:gotoloop
Agora estamos na etapa 4 e fazemos um loop até que uma linha contenha "bar"/bar/
loop for concluído, "barra" foi encontrada, é o espaço do padrãog
o espaço do padrão é substituído pelo espaço de espera que contém todas as linhas entre "foo" e "bar" que foram salvas durante o loop principalp
copie o espaço padrão para a saída padrãoFeito !
laço multiline sed
fonte