Substitua a menor correspondência de um padrão de string

2

Eu tenho esta string:

update mytable set mycol=myvalue where mycol=yourvalue;

Eu preciso convertê-lo para:

insert into mytemp select * from mytable where mycol=youvalue;

Eu posso realizar isso assim e funciona muito bem:

sed -e 's/^Update.*where//ig' -e "s/^/insert into mytemp select * from mytable where  /g" n.txt

MAS :

Se a string for:

update mytable set mycol=myvalue where mycol=(select yourcol from yourtable where youcol=yourvalue);

Eu recebo:

insert into mytemp select * from mytable where youcol=yourvalue);

Considerando que eu quero:

insert into mytemp select * from mytable where mycol=(select yourcol from yourtable where youcol=yourvalue);

O que posso fazer?

    
por Nirmal Arri 18.09.2012 / 21:09

2 respostas

3

Por padrão, o mecanismo de regex do sed é ganancioso. Isso significa que um padrão sempre corresponde à correspondência mais longa possível. Você deve fazer uma pesquisa não-gananciosa, mas acho que o sed não suporta buscas não gananciosas. Portanto, você deve adicionar um (s) ponto (s) de giro ao seu padrão de pesquisa para que sed encontre a correspondência mais curta possível.

A linha a seguir tenta emular a correspondência não-voraz para o seu caso especial e não exige universalidade, pois um único w entre update e o primeiro where torna o padrão inválido:

sed -e 's/^Update[^w]*where//ig'\
    -e "s/^/insert into mytemp select * from mytable where  /g" n.txt

Outros mecanismos de regex são compatíveis com esse recurso, como, por exemplo, o de perl e awk .

Mas no seu caso eu acho que uma expressão como essa

sed -e 's/^Update.\+where\(.\+where.*\)$/\
insert into mytemp select * from mytable where /ig'  n.txt

seria mais conveniente relacionado ao seu problema específico.

(o \ nas linhas acima só é adicionado para tornar as linhas mais legíveis).

    
por 18.09.2012 / 21:24
0

A correspondência de expressões regulares é executada da esquerda para a direita e com a correspondência mais longa feita na preferência. Portanto, ^Update.*where corresponde à última ocorrência de where na linha.

Uma maneira de fazer essa correspondência seria usar um quantificador não ganancioso para * . Sed não suporta quantificadores não-gulosos, mas perl faz.

perl -pe 's/^update.*?where//i; s/^/insert into mytemp select .*? from mytable where /'

Outra forma que pode ou não corresponder aos seus dados seria rejeitar parênteses no nome da tabela e nas configurações da coluna.

sed -e 's/^update[^()]*where//i' -e 's/^/insert into mytemp select [^()]* from mytable where /'

Um método mais elaborado seria primeiro substituir o primeiro where por um marcador único, depois fazer o seu substituto e, finalmente, voltar o marcador para where . Como o sed opera linha por linha, é garantido que uma linha não contém um caractere de nova linha, representado por \n em sed.

sed -e 's/ where /\n/' \
    -e 's/^update.*$//i' -e 's/^/insert into mytemp select .* from mytable where /' \
    -e 's/\n/ where/'
    
por 19.09.2012 / 02:37