Como substituir todas as primeiras ocorrências em linhas combinadas com um número incremental?

5

Eu tenho um arquivo como este

...
1562 first part
1563 H     col3 H col4
1564 H     col3 H col4
...
3241 H     col3 H col4
3242 third part
...

Eu quero substituir apenas o primeiro H em cada linha para H# , com # é o seu número de aparência. A saída deve ser:

...
1562 first part
1563 H1    col3 H col4
1564 H2    col3 H col4
...
3241 H1652 col3 H col4
3242 third part
...

Até agora, eu tentei:

max='grep -c ' H ' b'
while [[ "$i" -le $max ]];do
  grep -m $i ' H ' b|tail -n1|sed "s/H/H$i/1"
  let i=i+1
done

Esse código é lento, ele precisa ler todas as linhas para substituir e não pode adicionar a primeira parte e a terceira parte do arquivo. Existe alguma maneira melhor de fazer isso? Talvez awk? Obrigado.

    
por Ooker 18.08.2014 / 12:54

4 respostas

7

Você pode, por exemplo, usar isto:

$ awk '/H/{sub("H", "H"++v)}1' file
1562 first part
1563 H1     col3 H col4
1564 H2     col3 H col4

3241 H3     col3 H col4
3242 third part
...

Isso procura as linhas que contêm H e substitui H por H junto com uma variável que continuamos incrementando. Observe que você pode usar gsub() em vez de sub() se quiser realizar essa alteração em todos os padrões correspondentes em vez de apenas um.

O% final 1 é uma condição verdadeira, portanto, ele executa a ação awk padrão: {print $0} , ou seja, imprime a linha completa.

    
por 18.08.2014 / 13:03
1

Tente isto:

  awk 'BEGIN { hNum = 1; } { if ($2 == "H") { $2 = "H" hNum; hNum++; } print $0; }' yourFile > outFile

Ele executa awk usando espaço como separador, portanto, $2 é o segundo símbolo de cada linha e se $2 for igual a "H", substitua-o por "H" seguido por número iniciado por 1. Finalmente imprima a linha.

    
por 18.08.2014 / 13:12
1

com perl :

perl -pe 's/\bH\b\K/++$i/e' file

Você pode substituir -pe por -pi.back -e para edição no local com o original salvo como file.back ou -pi -e para nenhum backup.

    
por 18.08.2014 / 13:46
1
{   nl -bpH -w1 |
    sed 's/^\([0-9]*\)[ \t]*\([^H]*.\)//'
} <<\DATA
...
1562 first part 
1563 H     col3 H col4
1564 H     col3 H col4
...
3241 H     col3 H col4
3242 third part
DATA

OUTPUT

...
1562 first part 
1563 H1     col3 H col4
1564 H2     col3 H col4
...
3241 H3     col3 H col4
3242 third part

Esse é o modo mais rápido que posso imaginar que seria feito - especialmente com um arquivo muito grande. nl numerará apenas as linhas que contêm a string H e insere esse número no início da linha, seguido por um caractere <tab> . Recorta todas as outras linhas com alguns espaços.

sed é passado a saída de nl sobre o | pipe. Em seguida, sed substitui a seguinte sequência:

  • 0 ou mais dígitos que ocorrem no início da linha (referenciados como )
  • 0 ou mais <tab> ou <space> caracteres
  • 0 ou mais caracteres que não são H, então um caractere (referenciado como )

... com .

Portanto, as linhas que não contêm um H recebem este tratamento:

^''   .*.$ = ^.*.''$

E aqueles que fazem isso:

^(digit)*<tab>(not H)*H.*$ = ^(not H)*H(digit)*.*$

... onde '' é uma string vazia.

Para portabilidade máxima, você deve substituir o \t em [ \t] por um caractere literal <tab> .

    
por 18.08.2014 / 22:25