Manipula a última linha correspondente

2

Como manipulo a última linha correspondente em um texto? Eu tenho um arquivo de configuração no qual desejo alterar um determinado valor:

val1=0
val2=a
val3=?
val2=lol

Eu só quero alterar a última linha que corresponde ao valor para o grep. sed -i 's/\(^val2=\)/yes/' é um bom começo, mas substitui as duas linhas val2 em vez de apenas a última.

Eu marquei a questão muito genericamente como Unix porque estou aberto a qualquer solução: sed ou awk, Perl ou Ruby, Bash ou qualquer outra coisa. Uma solução mais curta é mais sexy do que uma boca cheia.

    
por wilhelmtell 10.09.2010 / 16:18

3 respostas

1

O melhor que eu encontrei é o seguinte:

tac config |while read LINE; do
  if [ "$DONE" != 1 ] && echo "$LINE" |grep '^\s*val2\s*=' >/dev/null; then
    echo "$LINE" |sed 's/^\(\s*val2\s*=\s*\)/yes/'
    DONE=1
  else
    echo "$LINE"
  fi
done |tac >config.new

Mas isso não parece os pipelines rápidos e sujos que eu gosto para uma tarefa tão comum.

    
por 10.09.2010 / 17:05
1

Com base no script encontrado aqui :

sed -r ':0;/^val2=/!b;:1;$!N;/\n.*word/{h;s/\n[^\n]*$//p;g;s/^.*\n//};$!b 1;s/^(.*)(val2=)[^\n]*/yes/' file
    
por 10.09.2010 / 16:52
1

Idéia geral: leia a entrada em blocos separados por \nval2= . Imprima todos os pedaços como estão, exceto pelo último trecho onde mudamos o texto até a primeira nova linha. Aqui está uma solução Gawk relativamente simples que lida com a maioria dos casos:

gawk -v 'RS=\nval2=' -v ORS= \
  'RT {print $0 RT} !RT {sub(/[^\n]*/, "yes"); print $0}'

Isso pressupõe que a primeira linha não comece com val2= e que haja pelo menos uma correspondência. Corrigir os dois defeitos complica significativamente o script:

gawk -v 'RS=\nval2=' -v ORS= \
  '!RT {if (NR==1) sub(/^val2=[^\n]*/, "val2=yes"); else sub(/[^\n]*/, "yes")}
   {print $0 RT}'

Aqui está uma solução Perl baseada no mesmo princípio geral, mas lendo o arquivo inteiro de uma só vez. Eu acho mais claro porque não tem que lidar com casos especiais (além do arquivo vazio).

perl -e 'undef $/;
         @chunks = split /(?=^val2=)/m, <>;
         $chunks[@chunks-1] =~ s/(?<=^val2=).*/yes/ if @chunks;
         print @chunks'

Com os regexps estendidos do Perl, podemos usar uma única substituição de expressão regular. Basta combinar o ^val2= que não é seguido por outro ^val2= :

perl -e 'undef $/; $_=<>; s/^val2=.*\n(?!.*\nval2=)/val2=yes\n/m; print'

Eu tentei tirar proveito do rsplit do Python, que pode separar os últimos N campos, mas desisti quando não consegui fazê-lo funcionar sem ser muito mais enigmático do que minhas soluções Perl.

    
por 10.09.2010 / 21:08

Tags