A ideia básica para resolver problemas como este é passar os dois arquivos para sed
. Primeiro as definições, que estão armazenadas no espaço de espera de sed
. Em seguida, cada linha do outro arquivo recebe o espaço de suspensão anexado e cada ocorrência de uma variável que pode ser encontrada repetida nas definições anexadas é substituída.
Aqui está o script:
sed '/^[A-Z_]*:=.*/{H;d;}
G
:b
s/$\([A-Z_]*\)\([^A-Z_].*\n:=\)\([^[:cntrl:]]*\)//
tb
P
d' Gnom.def form.txt
E agora a explicação detalhada:
/^[A-Z_]*:=.*/{H;d;}
Isso coleta as definições para o espaço de espera. /^[A-Z_]*:=.*/
seleciona todas as linhas que começam com um nome de variável e a seqüência :=
. Nessas linhas, os comandos em {}
são executados: O H
os anexa ao espaço de espera, o d
os exclui e recomeça, para que não sejam impressos.
Se você não puder assegurar que todas as linhas no arquivo de definição seguem esse padrão, ou se as linhas no outro arquivo puderem corresponder ao padrão fornecido, essa parte precisará ser adaptada, como explicado mais tarde.
G
Neste ponto do script, apenas linhas do segundo arquivo são processadas. O G
anexa o espaço de espera ao espaço de padrão, portanto, temos a linha a ser processada com todas as definições no espaço de padrão, separadas por novas linhas.
:b
Isso inicia um loop.
s/$\([A-Z_]*\)\([^A-Z_].*\n:=\)\([^[:cntrl:]]*\)//
Esta é a parte fundamental, a substituição. Agora temos algo como
At the $FOO<newline><newline>FOO:=bar<newline>BAR:=baz
----==================--- ###
no espaço do padrão. (Detalhe: há duas novas linhas antes da primeira definição, uma produzida anexando ao espaço de espera, outra anexando ao espaço de buffer.)
A parte sublinhada com ----
corresponde a $\([A-Z_]*\)
. O \(\)
torna possível retroceder a referência para essa sequência mais adiante.
\([^A-Z_].*\n\)
corresponde à parte sublinhada com ===
, que é tudo até a referência anterior . Começando com um não
O caractere n-variable garante que não combinemos substrings de uma variável. Envolvendo a referência anterior com uma nova linha e
:=
certifica-se de que uma subseqüência de uma definição não corresponderá.
Por fim, \([^[:cntrl:]]*\)
corresponde à parte ###
, que é a definição. Note que assumimos que a definição não possui caracteres de controle. Se isso for possível, você pode usar [^\n]
com GNU sed
ou fazer uma solução alternativa para POSIX sed
.
Agora, o $
e o nome da variável são substituídos pelo valor da variável , a parte do meio e a definição são deixadas como estavam:
.
tb
Se uma substituição foi feita, o comando t
faz um loop para marcar b
e tenta outra substituição.
P
Se nenhuma outra substituição for possível, o P
em maiúsculas imprime tudo até a primeira nova linha (assim, a seção de definição não será impressa) e
d
irá apagar o espaço padrão e iniciar o próximo ciclo. Feito.
Limitações
-
Você pode fazer uma coisa desagradável, como incluir
FOO:=$BAR
eBAR:=$FOO
no arquivo de definição e fazer o script girar para sempre. Você pode definir uma ordem de processamento para evitar isso, mas tornará o script mais difícil de entender. Deixe isso de lado, se o seu script não precisar ser uma prova idiota. -
Se a definição puder conter caracteres de controle, após o
G
, poderemos trocar a nova linha com outro caractere comoy/\n#/#\n
e repetir isso antes de imprimir. Não conheço uma solução melhor. -
Se o arquivo de definição puder conter linhas com formato diferente ou o outro arquivo puder conter linhas com formato de definição, será necessário um separador exclusivo entre os dois arquivos, como última linha do arquivo de definição ou como primeira linha do arquivo. outro arquivo ou como arquivo separado você passa para
sed
entre os outros arquivos. Em seguida, você tem um loop para coletar as definições até que a linha separadora seja atendida e, em seguida, faça um loop para as linhas do outro arquivo.