Use um arquivo de lista para editar um arquivo mestre através do awk

1

todos. Eu tenho dois arquivos: ports.lst e master.tbl

ports.lst se parece com isso:

hawaii-P1
hawaii-P2
hawaii-P3
losangeles-P1
losangeles-P3

master.tbl tem esta aparência:

#Site 1 Honolulu
servername HAWAII-A hawaii-P1 InitFileA OutFileA otherfields
servername HAWAII-A hawaii-P2 InitFileA OutFileA otherfields
#servername HAWAII-A hawaii-P3 InitFileA OutFileA otherfields
servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields

#Site 16 Dallas
servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields

#Site 8 L.A.
#servername LOSANGELES-A losangeles-P1 InitFileA OutFileA otherfields
servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields
#servername LOSANGELES-A losangeles-P3 InitFileA OutFileA otherfields

Eu preciso procurar o arquivo master.tbl para cada porta listada em ports.lst e substituir "InitFileA" e "OutFileA" deixando o arquivo com esta aparência:

#Site 1 Honolulu
servername HAWAII-A hawaii-P1 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
servername HAWAII-A hawaii-P2 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
#servername HAWAII-A hawaii-P3 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields

#Site 16 Dallas
servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields

#Site 8 L.A.
#servername LOSANGELES-A losangeles-P1 InitFileB-losangeles-username-ALPHA-password OutFileB-losangeles-username-ALPHA otherfields
servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields
#servername LOSANGELES-A losangeles-P3 InitFileB-losangeles-username-ALPHA-password OutFileB-losangeles-username-ALPHA otherfields

Aqui é onde eu estou agora, mas falha - claramente.

awk 'NR==FNR{z[$0];next}{if ($3 in z && $4 == "InitFileA"){ c=(echo $3| awk -F '-' {print $1});$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl

Eu também tentei:

awk 'NR==FNR{z[$0];next}{if ($3 in z && $4 == "InitFileA"){ c=$3; sub(/-.*/, "", $c);$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl

Eu tenho puxado meu cabelo sobre isso. Alguém aqui pode oferecer algum insight sobre o que estou fazendo errado?

    
por Chris Boone 22.11.2016 / 21:05

3 respostas

1

Você tem a idéia básica correta de dividir a tarefa em duas rodadas, mas então você invoca o awk dentro de uma regra do awk. É onde eu parei de ler; é uma maneira muito complicada de resolver um problema tão simples.

Considere este snippet do awk:

awk 'BEGIN {
         RS = "[\t\v\f ]*(\r\n|\n\r|\r|\n)";
         FS = "[\t\v\f ]+"
     }

     FNR==1 {
         file++
     }

     /^#/ {
         next
     }

     file==1 {
         port[$1] = $1
     }

     file>=2 && ($3 in port) {
         base = $3;
         sub(/-[^-]*$/, "", base);
         $4 = "InitFileB-" base "-username-ALPHA-password";
         $5 = "OutFileB-" base "-username-ALPHA";
     }

     file>=2 {
         printf "%s\n", $0
     } ' ports.lst master.tbl

Observação: adicionei os pontos-e-vírgulas necessários para que você possa escrever todos os itens acima em uma única linha.

Se você executar o exemplo acima com os arquivos de entrada de exemplo, obterá

losangeles-P1
losangeles-P3
servername HAWAII-A hawaii-P1 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
servername HAWAII-A hawaii-P2 InitFileB-hawaii-username-ALPHA-password OutFileB-hawaii-username-ALPHA otherfields
servername HAWAII-A hawaii-P4 InitFileA OutFileA otherfields

servername DALLAS-A dallas-P1 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P2 InitFileA OutFileA otherfields
servername DALLAS-A dallas-P3 InitFileA OutFileA otherfields

servername LOSANGELES-A losangeles-P2 InitFileA OutFileA otherfields

A regra BEGIN apenas define o suporte universal de nova linha, caso os arquivos tenham sido transferidos de algum outro sistema (Windows, por exemplo) com uma nova codificação de nova linha.

A regra FNR==1 é usada para atualizar a variável file , para que ela reflita o arquivo que está sendo processado (1 para primeiro, 2 segundos).

A regra /^#/ { next } ignora todas as linhas que começam com uma marca de hash. Eles são comentários, então eles não precisam ser mantidos. Poderíamos também adicionar uma regra /^[\t\v\f ]*$/ { next } para pular todas as linhas vazias, se você quisesse compactar o arquivo de saída.

A regra file == 1 { port[$1] = $1 } adiciona todos os primeiros campos do primeiro arquivo à matriz associativa port . O valor atribuído ( = $1 ) não importa, portanto, poderíamos usar apenas = 0 aqui.

A regra file >= 2 && ($3 in port) aplica-se ao segundo e aos arquivos a seguir e é executada se o terceiro campo corresponder a uma das chaves na matriz associativa port . (Os valores não importam; apenas as teclas são verificadas.) Em outras palavras, essa regra é aplicada apenas quando o terceiro campo é uma das chaves especificadas na lista de portas.

O terceiro campo é copiado para uma variável base - isso corresponde a uma das chaves em port[] - e tudo após o último - é removido usando sub() . Então, modificamos o quarto e quinto campos. Note que no awk não existe um operador de concatenação de strings; nós apenas declaramos as cordas próximas umas das outras. Em outras palavras, ("foo" a "bar") é uma string, consistindo de "foo", imediatamente seguida pelo valor da variável a convertido em uma string, imediatamente seguida por "bar".

A regra final imprime o registro (possivelmente modificado), mas garante que \n newline seja usada. Apenas os registros no segundo e nos arquivos subseqüentes são usados.

Agora, se ports.lst continha os nomes de usuário e senhas respectivos, eu modificaria um pouco o acima (talvez três linhas mudaram?), mas espero que você possa ver a abordagem geral.

    
por 22.11.2016 / 21:41
0

Parece que descobri a resposta. Meu problema parece ter sido na colocação dos cifrões, perto da variável 'c'. Isso funcionou:

awk 'NR==FNR{z[$0];next} { if ($3 in z && $4 == "InitFileA"){ c=$3; sub(/-.*/, "", c);$4="InitFileB-"c"-username-ALPHA-password";$5="OutFileB-"c"-username-ALPHA"}}1' ports.lst master.tbl > output.tbl

Agora, quanto ao POR QUE isso funcionou, receio não poder explicar. Eu estava literalmente agarrando canudos e ficando desesperado. Eu tinha recorrido a remover camadas do comando, até que o erro desaparecesse e, lentamente, adicionando comandos e mexendo até que cada um funcionasse.

    
por 22.11.2016 / 21:19
0

Eu não faço scripts awk suficientes para apenas digitar as instruções, mas eu procuraria uma maneira de usar a estrutura do arquivo 'master' e ter vários blocos.

Plano de solução conceitual

BEGIN
{
#  get it ready ...
}

/^$/
{
# maybe just skip lines
# otherwise potential post processing for #Site XX Name
}

/^#Site/
{
# initialize processing for a new site
}

{
# default block for the site processing 'input'
}

FINISH
{
# 'master' is parsed - now fill in the blanks using 'port'
# i.e, start of second pass to complete the work
}

Eu sei que não há comandos do awk nisso - mas também estou curioso para saber se os especialistas do awk vêem isso como uma abordagem genérica para as soluções do awk. Muitas vezes eu estou desligado para usar o awk porque eu me perco em 'instruções awk de uma linha', ou seja, um comando que vai processar todas as linhas, independentemente de haver blocos recorrentes de entrada.

E, mesmo que isso seja uma abordagem ridícula - espero que os comentários me ilumine (e aos outros) de tal forma que melhore com o awk. Obrigado!

    
por 25.11.2016 / 09:24