Usando sed para editar uma string em duas linhas

3

Eu tenho um arquivo que inclui linhas consecutivas como esta

macroa{abc def 123 ghi}
macrob{abc 123 xyz}

Eu quero verificar se a primeira string no macrob é a mesma que na macro e se é para apagá-lo, então o resultado é

macroa{abc def 123 ghi}
macrob{123 xyz}

Estou usando toda a abordagem de arquivos de aqui e meu comando é

sed -e '1h;2,$H;$!d;g' -e 's/\(macroa{\([a-z]*\) [^\n]*\)\n\(macrob{\) /\n/g' in > out

No entanto, isso não funciona. O que estou fazendo errado, obrigado.

    
por tom 20.02.2018 / 07:22

2 respostas

3

Eu testei seu script com o GNU sed e ele produziu o resultado esperado. No entanto, isso não é portável para outras versões de sed , já que você usa \n dentro de [] e na substituição, que é indefinida pelo padrão.

Usá-lo na substituição pode ser facilmente evitado:

sed -e '1h;2,$H;$!d;g' -e 's/\(macroa{\([a-z]*\) [^\n]*\)\(\nmacrob{\) //g'

Para usá-lo na expressão [] pode ser feito com um truque - você usa o comando y para trocar a nova linha com um caractere normal antes da substituição e alterá-lo novamente depois; neste caso eu uso | :

sed -e '1h;2,$H;$!d;g' -e 'y/\n|/|\n/;s/\(macroa{\([a-z]*\) [^|]*\)\(|macrob{\) //g;y/\n|/|\n/'

Esta é a solução universal, mas acho que é feia. Na maioria dos casos, em vez de [^\n] , você pode escrever [[:print:]] , porque normalmente todo o código, exceto as novas linhas, consiste em caracteres imprimíveis, portanto, é:

sed 'H;1h;$!d;g;s/\(macroa{\([a-z]*\) [[:print:]]*\)\n\(macrob{\) /\n/g'

(também simplifiquei seu 1h;2,$H inicial para H;1h .)

Considerando o comentário de don_crissti, acrescento que a abordagem típica para resolver esse tipo de problema é o ciclo N;P;D : sempre adicione a linha N ext, processe os dois juntos, P rint a primeira linha e D elete a partir do espaço padrão para continuar com o segundo:

sed 'N;s/\(macroa{\)\([a-z]* \)\(.*\nmacrob{\)//;P;D'
    
por 20.02.2018 / 07:55
2

Se estiver tudo bem em usar awk em vez de sed

$ awk -F'[{ ]' 'c && c-- && $1=="macrob" && $2==s{sub(s" ", "")}
                $1=="macroa"{c=1; s=$2} 1' ip.txt
macroa{abc def 123 ghi}
macrob{123 xyz}
  • -F'[{ ]' use { ou caractere de espaço como separador de campo
  • $1=="macroa"{c=1; s=$2} se o primeiro campo for macroa , inicialize o contador com 1 e salve o segundo campo em uma variável. O contador determina quais linhas seguintes devem ser verificadas
  • c && c-- isso será verdadeiro enquanto o contador for diferente de zero. Como c=1 neste caso, somente quando isso for verdadeiro e independentemente de outras condições, o contador se tornará zero. Portanto, apenas linhas consecutivas podem corresponder a
  • $1=="macrob" && $2==s condição exigida
    • sub(s" ", "") remove a string e um caractere de espaço
  • Outras leituras: Imprimindo com sed ou awk uma linha seguindo um padrão correspondente
por 20.02.2018 / 09:22

Tags