pt - executa somente a primeira substituição (n) correspondente?

3

Considere que tenho o seguinte arquivo:

echo "1
2
3
4
1
2
3
4
1
2
3
4
1
2
3
4
" > ztest

Aqui, gostaria de alterar apenas o primeiro 1 para 5 e deixar todo o restante intacto.

Eu sei que o sed tem um comando quit - então estou tentando o seguinte:

$ sed 's/1/{5;q}/' ztest 
{5;q}
2
3
4
{5;q}
2
3
4
{5;q}
2
3
4
{5;q}
2
3
4

Isso muda todas as linhas, então não é bom. Então, eu tento isso:

$ sed '{s/1/5/;q}' ztest 
5

Agora, isso aparentemente acontece conforme solicitado - altera a primeira linha e sai; no entanto, gostaria que o resto das linhas permanecesse intacto! ( desde que, basicamente, tem o efeito de substituir o arquivo inteiro com um único '5' )

Então estou perdendo em que tipo de sintaxe é necessário? Note que eu preciso disso para alterar uma linha "inicial" em um arquivo de gigabyte em linha (usando sed -i ); assim, eu gostaria que sed pesquisasse e substituísse apenas na primeira seção breve - e depois disso, as linhas de saída inalteradas ( pois sed -i de qualquer forma despeja em um arquivo temporário primeiro e depois substitui o original ); na esperança de economizar algum tempo de processamento.

Agradecemos antecipadamente por qualquer resposta,
Felicidades!

EDIT: esqueci a subquestão: é possível estender isso para as primeiras n correspondências? (digamos, no arquivo acima, os dois primeiros '1's são alterados para 5 - o restante é produzido literalmente?)

    
por sdaau 26.02.2012 / 19:37

5 respostas

6

Eu já vi em outro lugar neste site:

sed '/1/{s/1/5/;:a;n;ba}' ztest

Então, uma vez que você encontrou a primeira ocorrência, você faz o loop até o final das linhas de leitura e impressão de arquivos.

Atualizar

Pode ser melhorado apenas substituir a enésima partida. A idéia é armazenar um X em cada correspondência no espaço de suspensão, e quando todos os X s estiverem lá, faça um loop até o final do arquivo. Abaixo, o script que substitui a segunda ocorrência:

sed '/1/{G;s/\nX\{1\}//;tend;x;s/^/X/;x;P;d};p;d;:end;s/1/5/;:a;n;ba'

Note que você precisa colocar (N-1) entre o \{ e o \} .

Atualização 2

Sei que o P;d acima é inútil se o p;d for substituído por P;d . Então, uma solução mais simples é:

sed '/1/{G;s/\nX\{1\}//;tend;x;s/^/X/;x};P;d;:end;s/1/5/;:a;n;ba'
    
por 26.02.2012 / 20:21
3

Não sei se isso é possível com sed , mas aqui está uma versão awk .

awk 'BEGIN {matches=0}
     matches < 2 && /1/ { sub(/1/,"5"); matches++ }
     { print $0 }' ztest 

Isso substituirá 1 por 5 nas duas primeiras correspondências de 1 . (Altere o 2 para aumentar / diminuir o número de correspondências desejado). Depois disso, o arquivo é impresso como está.

    
por 26.02.2012 / 19:40
2

Isso pode funcionar para você (GNU sed):

sed '0,/1/s//5/' file
    
por 26.02.2012 / 21:55
1

Use o padrão :a;n;ba mais uma vez:

$ sed '/1/{s//5/;:A;/1/{s//5/;:B;n;bB};n;bA}'
    
por 27.02.2012 / 03:59
0

Graças à primeira resposta do @Mat (no StackOverflow, agora excluída), a sintaxe para sed para substituir apenas a correspondência na primeira linha é:

sed  '1s/1/5/' ztest

No entanto, você precisa saber explicitamente o número da linha onde realizar a correspondência.

Então, se eu souber que o arquivo é como acima, os dois primeiros 1 aparecerão nas linhas 1 e 5; Assim, para as duas primeiras correspondências, eu poderia instruir sed a aplicar a expressão entre as linhas 1 e (digamos) 6 - a sintaxe da linha (endereço) em sed sendo uma vírgula ( , ):

$ sed  '1,6s/1/5/' ztest
5
2
3
4
5
2
3
4
1
2
3
4
1
2
3
4

Da mesma forma, a segunda e terceira ocorrência de 1 estão nas linhas 5 e 9 - então eu posso usar o intervalo da linha 3 para a linha 10 para alterar a segunda e a terceira ocorrência de 1 :

$ sed  '3,10s/1/5/' ztest
1
2
3
4
5
2
3
4
5
2
3
4
1
2
3
4

.... o que eu acho que responde a minha pergunta em termos de sed ; embora eu esperasse poder especificar o número de correspondências diretamente ( como na solução awk por @Mat ).

    
por 26.02.2012 / 20:51

Tags