Eu quero usar sed
para substituir qualquer coisa em uma seqüência de caracteres entre a primeira AB
e a primeira ocorrência de AC
(inclusive) por XXX
.
Por exemplo , eu tenho essa sequência (essa sequência é apenas para um teste):
ssABteAstACABnnACss
e eu gostaria de saída semelhante a esta: ssXXXABnnACss
.
Eu fiz isso com perl
:
$ echo 'ssABteAstACABnnACss' | perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss
mas eu quero implementá-lo com sed
. O seguinte (usando o regex compatível com Perl) não funciona:
$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss
text-processing
sed
regular-expression
بارپابابا
fonte
fonte
Respostas:
Regexes sed correspondem à correspondência mais longa. Sed não tem equivalente de não ganancioso.
Obviamente, o que queremos fazer é combinar
AB
,seguido por
AC
,seguida por
AC
Infelizmente,
sed
não é possível fazer o número 2 - pelo menos não para uma expressão regular com vários caracteres. Obviamente, para uma expressão regular de um caractere, como@
(ou mesmo[123]
), podemos fazer[^@]*
or[^123]*
. E assim podemos contornar as limitações do sed, alterando todas as ocorrênciasAC
para@
e, em seguida, à procura deAB
,seguido por
@
,seguido de
@
como isso:
A última parte altera instâncias sem correspondência de
@
volta paraAC
.Mas, é claro, essa é uma abordagem imprudente, porque a entrada já pode conter
@
caracteres; assim, combinando-os, podemos obter falsos positivos. No entanto, como nenhuma variável do shell terá um caractere NUL (\x00
), é provável que o NUL seja um bom caractere a ser usado na solução alternativa acima, em vez de@
:O uso de NUL requer GNU sed. (Para garantir que os recursos GNU estejam ativados, o usuário não deve ter definido a variável de shell POSIXLY_CORRECT.)
Se você estiver usando sed com o
-z
sinalizador do GNU para lidar com entradas separadas por NUL, como a saída defind ... -print0
, então NUL não estará no espaço do padrão e NUL é uma boa opção para a substituição aqui.Embora o NUL não possa estar em uma variável do bash, é possível incluí-lo em um
printf
comando. Se sua string de entrada pode conter qualquer caractere, incluindo NUL, consulte a resposta de Stéphane Chazelas, que adiciona um método de escape inteligente.fonte
echo
ouprintf
um `\ 000 'bem no bash (ou a entrada pode vir de um arquivo). Mas, em geral, é claro que uma sequência de texto provavelmente não possui NULs.AC
paraAC@
e para trás novamente?Algumas
sed
implementações têm suporte para isso.ssed
tem um modo PCRE:A AT&T ast sed possui conjunção e negação ao usar regexps aumentados :
Portably, você pode usar esta técnica: substitua a sequência final (aqui
AC
) por um único caractere que não ocorre na sequência inicial ou final (como:
aqui) para que você possa fazê-los/AB[^:]*://
, e caso esse caractere possa aparecer na entrada , use um mecanismo de escape que não colidir com as seqüências de início e final.Um exemplo:
Com o GNU
sed
, uma abordagem é usar a nova linha como o caractere de substituição. Comosed
processa uma linha de cada vez, a nova linha nunca ocorre no espaço do padrão; portanto, é possível:Isso geralmente não funciona com outras
sed
implementações porque elas não suportam[^\n]
. Com o GNU,sed
você deve garantir que a compatibilidade do POSIX não esteja ativada (como na variável de ambiente POSIXLY_CORRECT).fonte
Não, as expressões regulares sed não têm correspondência não gananciosa.
Você pode corresponder todo o texto até a primeira ocorrência
AC
usando "qualquer coisa que não contenhaAC
" seguida deAC
, que faz o mesmo que o Perl.*?AC
. O fato é que "qualquer coisa que não contenhaAC
" não pode ser expressa facilmente como uma expressão regular: sempre há uma expressão regular que reconhece a negação de uma expressão regular, mas o regex de negação fica complicado rapidamente. E no sed portátil, isso não é possível, porque o regex de negação requer o agrupamento de uma alternância que está presente em expressões regulares estendidas (por exemplo, no awk), mas não em expressões regulares básicas portáteis. Algumas versões do sed, como o GNU sed, têm extensões para o BRE que permitem expressar todas as expressões regulares possíveis.Devido à dificuldade de negar uma regex, isso não generaliza bem. O que você pode fazer é transformar a linha temporariamente. Em algumas implementações sed, é possível usar as novas linhas como marcador, pois elas não podem aparecer em uma linha de entrada (e se você precisar de vários marcadores, use a nova linha seguida por um caractere variável).
No entanto, lembre-se de que a barra invertida-newline não funciona em um conjunto de caracteres com algumas versões sed. Em particular, isso não funciona no GNU sed, que é a implementação sed no Linux não incorporado; No GNU sed, você pode usar
\n
:Nesse caso específico, basta substituir o primeiro
AC
por uma nova linha. A abordagem que apresentei acima é mais geral.Uma abordagem mais poderosa no sed é salvar a linha no espaço de espera, remover tudo, exceto a primeira parte "interessante" da linha, trocar o espaço de espera e o espaço do padrão ou anexar o espaço do padrão ao espaço de espera e repetir. No entanto, se você começar a fazer coisas complicadas, pense em mudar para o awk. O Awk também não possui correspondência não gananciosa, mas você pode dividir uma string e salvar as partes em variáveis.
fonte
s/\n//g
remove todas as novas linhas.sed - correspondência não gananciosa de Christoph Sieghart
fonte
No seu caso, você pode simplesmente negar o fechamento do char desta maneira:
fonte
AB
e a primeira ocorrência deAC
comXXX
..." e fornecessABteAstACABnnACss
como exemplo de entrada. Esta resposta funciona para esse exemplo , mas não responde à pergunta em geral. Por exemplo,ssABteCstACABnnACss
também deve produzir a saídaaaXXXABnnACss
, mas seu comando passa essa linha inalterada.A solução é bastante simples.
.*
é ganancioso, mas não é absolutamente ganancioso. Considere a correspondênciassABteAstACABnnACss
com a regexpAB.*AC
. OAC
que se segue.*
deve realmente ter uma correspondência. O problema é que, por.*
ser ganancioso, o subsequenteAC
corresponderá ao últimoAC
e não ao primeiro..*
come o primeiroAC
enquanto o literalAC
no regexp corresponde ao último em ssABteAstACABnn AC ss. Para impedir que isso aconteça, basta substituir o primeiroAC
por algo ridículo para diferenciá-lo do segundo e de qualquer outra coisa.O ganancioso
.*
vai agora parar no pé de-foobar-
nossABteAst-foobar-ABnnACss
porque não há nenhum outro-foobar-
do que isso-foobar-
, eo regexp-foobar-
deve ter um jogo. O problema anterior era que o regexpAC
tinha duas correspondências, mas por.*
ser ganancioso, a última correspondênciaAC
foi selecionada. No entanto, com-foobar-
, apenas uma correspondência é possível, e esta prova que.*
não é absolutamente gananciosa. A parada de ônibus para.*
ocorre onde resta apenas uma correspondência para o restante da regexp a seguir.*
.Observe que esta solução falhará se um
AC
aparecer antes da primeiraAB
porque o erradoAC
será substituído por-foobar-
. Por exemplo, após a primeirased
substituição,ACssABteAstACABnnACss
torna-se-foobar-ssABteAstACABnnACss
; portanto, não é possível encontrar uma correspondênciaAB.*-foobar-
. No entanto, se a sequência for sempre ... AB ... AC ... AB ... AC ..., essa solução será bem-sucedida.fonte
Uma alternativa é mudar a string para que você queira a combinação gananciosa
Use
rev
para inverter a corda, inverta seus critérios de correspondência, usesed
da maneira usual e depois inverta o resultado ....fonte