torr / awk substitui um padrão específico sob outro padrão

1

Então eu tenho um arquivo assim

[ABC]
value1=bla
value2=bla
value3=bla
[XYZ]
value1=bla
value2=bla
value3=bla

Eu gostaria de substituir o valor1 no bloco [ABC] por "valor1 = notbla" e não por baixo do bloco [XYZ]. Eu já tentei

sed '/ABC/{n;s/.*/change/}'  file

Mas isso só funcionaria ao tentar ir para a próxima linha e não mudaria o padrão específico (ou seja, se o valor 1 estivesse abaixo do valor 2) Qual comando sed ou awk eu poderia usar se não soubesse os números de linha específicos. E você pode, por favor, rotular a função de cada tag sed ou awk usada. Eu realmente aprecio isso.

    
por Manseej Khatri 08.10.2016 / 00:47

3 respostas

2

Sed consegue lidar com isso com bastante facilidade. É um único comando "substituto", prefixado com um intervalo de endereços. Eu adicionei espaçamento extra para melhor legibilidade:

sed -e '/^\[ABC\]$/ , /^\[.*\]$/     s/^\(value1=\).*$/notbla/'

Sem o espaçamento extra, é:

sed -e '/^\[ABC\]$/,/^\[.*\]$/s/^\(value1=\).*$/notbla/'

Você realmente não precisa de expressões regulares ancoradas, mas elas podem ser mais seguras em alguns casos de entradas incomuns. Uma versão ligeiramente mais curta com expressões regulares sem âncora é:

sed -e '/\[ABC\]/,/^\[/s/^\(value1=\).*$/notbla/'

Explicação:

Você pediu que cada bandeira ou opção fosse explicada, e eu tenho tempo, então aqui vai você. Estou explicando a versão final (mais curta) dos três comandos Sed listados acima.

A primeira parte da linha é um intervalo de endereços: /startregex/,/stopregex/ O comando s ubstitute, que segue o intervalo de endereços, é aplicado apenas às linhas de startregex a stopregex (inclusive).

Nesse caso, a regex inicial é /\[ABC\]/ . Os colchetes são normalmente caracteres especiais dentro de uma expressão regular, por isso colocamos uma barra invertida antes de cada um para indicar caracteres literais de colchetes literais.

A regex de parada é /^\[/ , que usa o caractere de regex especial ^ para indicar o início de uma linha. Esse padrão corresponderá a qualquer linha que comece com um colchete esquerdo literal ( [ ).

O comando s ubstitute é basicamente bastante simples; o formato geral é s/findregex/replacetext/ . Ele também pode ter sinalizadores especiais colocados após o / final para modificar seu comportamento, mas não estou usando nenhum desses sinalizadores aqui.

A "regex encontrada" é ^\(value1=\).*$ .

O cursor ( ^ ) corresponde ao início da linha, como mencionado anteriormente, e o sinal de dólar ( $ ) corresponde ao final da linha. Portanto, todo esse padrão deve corresponder a uma linha inteira, não apenas parte de uma.

Os parênteses ( () ), ao contrário dos colchetes, são non -especiais por padrão nas expressões regulares, então colocamos as barras invertidas antes deles para dar a eles um significado especial. Eles permitem que partes do texto correspondente (o texto correspondido pelo "find regex") sejam usadas no texto de substituição. Especificamente, o no texto de substituição significa "O texto correspondido no primeiro conjunto de parênteses na expressão regular". Neste caso, isso é sempre apenas "valor1=".

O elemento final no "find regex" é .* . O ponto ( . ) significa "qualquer caractere único" e o asterisco ( * ) significa "qualquer número de vezes (zero ou mais)". Então, a estrela do ponto ( .* ) corresponde ao resto inteiro da linha, após o sinal de igual.

"notbla" no texto de substituição é apenas texto estático, nada de especial.

Para realmente aprender Sed corretamente, eu recomendo o tutorial do Grymoire Sed , que é gratuito on-line.

    
por 08.10.2016 / 03:35
2
awk '
  BEGIN {FS=OFS="=";}
  /\[.*\]/ {if ($0 == "[ABC]") edit=1; else edit=0;}
  {if (edit && $1 == "value1" ) $2="notbla";}1
' file 
    
por 08.10.2016 / 01:34
2

outra awk solution

$ cat ip.txt 
[ABC]
value1=bla
value2=bla
value3=bla
[XYZ]
value1=bla
value2=bla
value3=bla

Se a linha começar com [ , atribua um sinalizador que será definido ou limpo com base no nome do bloco. Então, se o sinalizador estiver definido, faça a substituição

$ awk '/^\[/{f=/\[ABC\]/} f{sub("value1=bla","value1=nobla")} 1' ip.txt 
[ABC]
value1=nobla
value2=bla
value3=bla
[XYZ]
value1=bla
value2=bla
value3=bla


Solução semelhante em perl , alterou a substituição para demonstração

$ perl -pe '$f=/\[ABC\]/ if /^\[/; s/value2=bla/value2=nobla/ if $f;' ip.txt 
[ABC]
value1=bla
value2=nobla
value3=bla
[XYZ]
value1=bla
value2=bla
value3=bla


Se tudo para a direita de = tiver que ser alterado, independentemente de corresponder especificamente a bla , use

awk '/^\[/{f=/\[ABC\]/} f{sub(/value1=.*/,"value1=nobla")} 1' ip.txt 
perl -pe '$f=/\[ABC\]/ if /^\[/; s/value2=.*/value2=nobla/ if $f;' ip.txt
    
por 08.10.2016 / 03:25

Tags