Substitua um padrão se antes de um segundo padrão - mantenha a cadeia entre os dois

2

Aqui está o meu código:

printf '%s' '{$aaa} \{$bbb} \{$ccc}  {$ddd' | sed -r -e 's/(^|[^\])\{\$/\$\{/g' -e 's/\\\{\$/\\\$\{/g'

e o resultado:

${aaa} \${bbb} \{$ccc} ${ddd

O resultado é OK, exceto pelo último padrão, {$ddd , que não deve ser substituído porque não termina com colchetes de fechamento } .

Como posso fazer isso com sed?

Além disso, se você souber como mesclar as duas expressões reais, também será bem-vindo (não é tão importante para mim)

Editar: observe que o mesmo também é feito para os parênteses, ($foo) to $(foo)

    
por J.R. 01.05.2017 / 15:58

2 respostas

1

Começando com a primeira tarefa, apenas execute a alteração para {} :

printf '%s' '{$aaa} \{$bbb} \{$ccc}  {$ddd' | sed 's/{$\([^}]*\)}/${}/g'

Isto é, tudo após o {$ até o fechamento } é colocado em \(\) , para que possamos referenciá-lo na string de substituição como . Saída:

${aaa} \${bbb} \${ccc}  {$ddd

Agora, sobre o escape da barra invertida. Sua solução funciona para \{$aaa} (não toque) e \{$aaa} (substituir), mas e quanto a \\{$aaa} ? Ele executará a alteração, o que provavelmente não é desejado.

Por isso, sugiro se livrar de todas as barras duplas invertidas primeiro. Nós os substituímos por uma combinação de caracteres que não pode fazer parte da string, #1 como exemplo, e alteramos de volta no final. Dessa forma, podemos nos concentrar apenas na barra invertida simples:

printf '%s' '{$aaa} \{$bbb} \{$ccc} \\{$eee} {$ddd' | sed 's/\\/#1/g;s/{$\([^}]*\)}/${}/g;s/#1/\\/g'

E, finalmente, podemos alterar \{ para #2 e substituí-lo no final para sair do nosso caminho:

printf '%s' '{$aaa} \{$bbb} \{$ccc} \\{$eee} {$ddd' | sed 's/\\/#1/g;s/\{/#2/g;s/{$\([^}]*\)}/${}/g;s/#2/\{/g;s/#1/\\/g'

Resultado: ${aaa} \${bbb} \{$ccc} \\{$eee} {$ddd

E isso funcionará para qualquer número de barras invertidas.

Mas se não houver nenhum caractere como # você pode usar livremente? Você pode usar a nova linha, porque uma nova linha não pode fazer parte de uma linha. Já que você parece usar o GNU sed , você pode fazer

printf '%s' '{$aaa} \{$bbb} \{$ccc} \\{$eee} {$ddd' | sed 's/\\/\n1/g;s/\{/\n2/g;s/{$\([^}]*\)}/${}/g;s/\n2/\{/g;s/\n1/\\/g'

Se isso for executado em um POSIX sed , você precisará de um solução alternativa

    
por 01.05.2017 / 18:36
1

Usando POSIX sed , você pode executar sua tarefa da seguinte forma:

sed -e '
   # case for {$var}
   s/ \(\(\\\)\{0,\}\){\$\([^ }]*\)}/ ${}/g
   s/^\(\(\\\)\{0,\}\){\$\([^ }]*\)}/${}/

   # case for ($var)
   s/ \(\(\\\)\{0,\}\)(\$\([^ )]*\))/ $()/g
   s/^\(\(\\\)\{0,\}\)(\$\([^ )]*\))/$()/
' input_file

Assumindo espaços, o único espaço em branco no arquivo. É claro que podemos lidar com o TAB e deixar isso como um exercício para o OP. Com as extensões disponíveis em GNU sed , podemos simplificar ainda mais o acima, mas eu queria dar uma solução POSIX para dar maior aplicabilidade.

    
por 02.05.2017 / 06:21