Expressão regular para substituir uma instância de duas cadeias consecutivas que podem ser separadas por espaços em branco

4

Eu quero escrever um perl one-liner que substitua todas as instâncias de duas strings consecutivas específicas que podem ou não ser separadas por espaços em branco.

Por exemplo, digamos que minhas duas strings são john paul e george e quero substituir instâncias consecutivas dessas cadeias (nesta ordem) por pete . Executando o one-liner em

$ cat ~/foo

john paulgeorge
john paul george
john paul

    george

george john paul

deve resultar em

$ cat ~/foo

pete
pete
pete

george john paul

A única coisa em que pensei é

$ perl -p -i -e 's/john paul\s*george/pete/g' ~/foo

mas isso resulta em

$ cat ~/foo

pete
pete
john paul

    george

george john paul

Existe uma maneira de alterar meu one-liner?

    
por Brian Fitzpatrick 15.01.2016 / 20:52

5 respostas

5

A única coisa que você precisa adicionar ao seu one-liner é a opção de fazer o slurp do arquivo como uma única string:

perl -0777 -p -i -e 's/john paul\s*george/pete/g' ~/foo
#    ^^^^^

Veja o link

    
por 15.01.2016 / 23:20
4
As opções de perl -n e -p de

while (<>) { ... } colocam variantes de $/ em torno de seu programa, o que faz com que elas processem entrada de forma linear. Se você quiser substituir por várias linhas, precisa ler tudo em uma string, o que precisa fazer sozinho.

perl -e 'local $/;$_=<>;s/john paul\s*george/pete/g;print'

Isso não define <> , o separador de registros , para que $_ slurping não fará mais divisões de linha, lerá toda a entrada em -i de uma só vez e então faz a substituição nessa longa string. Você tem que fazer sua própria impressão também.

Não há mais mágica aqui - é só escrever um programa Perl completo de uma maneira um pouco desconfortável. O perldoc -q 'entire file' ainda funcionará para substituição no local.

Se você tiver um arquivo grande, isso será bastante ineficiente (ou esgotará sua memória), mas isso parece mais ou menos inevitável sem construir um analisador melhor. Você também pode ver %code% para outras alternativas e muito de dizer que você não está falando sério.

    
por 15.01.2016 / 21:43
3

Com sed , você pode fazer isso sem incluir o arquivo inteiro:

sed -e ':top' -e 's/john paul[[:space:]]*george/pete/g;$b' -e '/john paul[[:space:]]*$/!b' -e 'N;btop' input

Isso é muito mais leve no uso de memória; ele apenas absorve várias linhas quando existe a possibilidade de uma correspondência de várias linhas a partir da linha atual. E então, ele só vai comer até que a correspondência seja encontrada, ou até que não haja mais possibilidade de um jogo.

Como bônus, é compatível com POSIX. (Perl não faz parte do POSIX.) Obrigado ao mikeserv por apontar isso nos comentários.

Explicação:

:top define um rótulo chamado top .

s/john paul[[:space:]]*george/pete/g faz a substituição desejada para o que estiver no espaço do padrão. (O padrão é linha por linha).

$b pula para o final e imprime se a linha atual for a última linha do arquivo.

/john paul[[:space:]]*$/!b :

O padrão /john paul[[:space:]]*$/ irá corresponder a john paul no final do espaço padrão seguido por qualquer quantidade de espaço em branco (mas nada além de espaço em branco), então ! inverte o padrão. Portanto, o efeito aqui é executar o comando b (pule para o final do script, imprimindo assim o espaço padrão, lendo a próxima linha do arquivo e iniciando no topo do script) somente se não houver possibilidade de uma correspondência de várias linhas começando com o espaço de padrão atual.

N acrescenta a próxima linha do arquivo ao espaço padrão (após acrescentar uma nova linha).

btop ramifica para o rótulo :top sem limpar o espaço padrão.

    
por 15.01.2016 / 22:27
0

Outro sed :

s=[:space:]
sed -e:t -e$\!"N;s/john paul[$s]*george/pete/g;/\n/"\!tt -e"P;D" <in >out

Isso cuidará de qualquer / todas as ocorrências de sua string em uma única substituição, e armazenará apenas o mínimo que for absolutamente necessário. Ele funciona através de uma janela deslizante na entrada, e somente ramifica de volta para puxar em novas linhas se a substituição anterior substituiu com sucesso sua string e, como resultado, removeu um caractere de nova linha no processo.

A estranha ! de cotação é necessária apenas em um shell (ba|z|t?c)sh interativo (leia-se: insano) %, mas geralmente não é um problema em um shell com script (a menos que você ' ve tem uma variante csh .

    
por 16.01.2016 / 00:20
0

Você precisará fazer o slurp do arquivo com a opção -0777. Mas você também deve adicionar os modificadores m no final para ter certeza de que \ s também corresponderá ao \ n.

Quando Perl ver -0, ele atualizará o separador de registro de entrada ($ /) com o que vem a seguir. Por exemplo, se eu tivesse colocado -00, o Perl teria colocado o $ / no modo de parágrafo. Então

perl -0777 -pe 's/^john paul\s*george/pete/gm' george.txt

é equivalente a:

perl  -pe 'BEGIN { undef $/ ; } s/^john paul\s*george/pete/gm' george.txt 
    
por 12.08.2016 / 07:06

Tags