O awk pode tratar valores em registros de maneira diferente com base no primeiro valor?

1

Sou relativamente novo no awk. Estou criando um script awk que lerá um arquivo com o formato geral:

NAME firstName lastName
PAY cost numberOfPayments
END

Meu arquivo será parecido com isto:

NAME Jane Doe
PAY 5.00 2
PAY 2.00 10
END
NAME John Doe
PAY 10.00 5
PAY 4.00 3
PAY 1.00 20
END

A quantidade de pagamentos entre NAME e END pode variar e pode haver vários nomes (isso é apenas uma amostra).

Este é o meu script awk:

# !/bin/awk

BEGIN { total=0; RS = "END"; }
{
    if (match(, "NAME")) {
        print ;
    }
    if (match(, "SAVE")) {
        total = total + ( * );
        print total;
    }
}

O primeiro valor deve reconhecer qual ação estamos realizando ( PAY vs. NAME ). Com base nisso, eu deveria imprimir NAME ou encontrar o valor total encontrado multiplicando o custo pelo número de pagamentos. END é o que estou usando para indicar que esse é o fim do registro para esse cliente específico.

A saída deste arquivo em particular deve ser:

Jane 30
John 82

Eu tentei várias maneiras, mas não consigo obter a saída desejada. Qualquer ajuda para conseguir este trabalho seria muito apreciada!

    
por Stephanie 26.09.2017 / 03:48

1 resposta

1

Primeiro, o código:

#!/usr/bin/awk -f

 == "NAME" { printf "%s ",  }
 == "PAY" { total +=  *  }
 == "END" { print total; total = 0 }

Se você chamar o script tally , marcá-lo como executável com chmod +x tally e estiver no diretório que o contém, poderá executá-lo no arquivo de entrada file com:

./tally file

No texto de entrada que você mostrou, ele mostra a saída desejada:

Jane 30
John 82

Você não declarou como deseja que a saída se pareça quando houver vários nomes sem END entre, mas suponho que você deseje produzir o primeiro nome para cada um. Considere este arquivo de entrada:

NAME Jane Doe
NAME Clark Kent
PAY 5.77 9
END
NAME John Doe
PAY 14.22 6
NAME Linda Lee Danvers
PAY .25 4
END

Isso produz esta saída:

Jane Clark 51.93
John Linda 86.32

O que faz e por quê:

No problema que você está tentando resolver, cada uma das quais deve ser conceitualmente considerada como um registro é uma "estrofe" de várias linhas, em que uma linha pode consistir em vários campos. Cada dado tem, portanto, três "coordenadas": ⟨stanza, linha, campo⟩

Mas a abstração fundamental do AWK é ⟨record, field⟩ . O AWK ainda é uma boa escolha para esse problema, mas você terá que decidir como deseja mapear a abstração natural do problema para a abstração que sua ferramenta suporta diretamente. Em seu código, parece que você pode estar tentando tratar cada sub-rotina como um único registro, já que você criou END o separador de registro de entrada ( RS = "END" ). Isso pode ser feito para funcionar, e espero que outras respostas sejam postadas mostrando como. Mas sugiro que o awk trate cada linha como um registro.

A razão é que já existe outra maneira de pensar sobre seus dados de entrada: como uma lista de comandos , um por linha, onde:

  1. Seu comando NAME exibe a palavra que segue. Conceitualmente, este é o primeiro nome.
  2. Seu comando PAY acumula produtos em uma variável total . Especificamente, ele multiplica os dois valores que o seguem e aumenta total dessa quantia.
  3. Seu comando END imprime total , termina a linha e redefine total de volta para zero.

Como funciona, linha por linha:

#!/usr/bin/awk -f

No Ubuntu, awk está localizado em /usr/bin e não /bin . O -f flag é necessário (em qualquer sistema operacional) para informar ao AWK que o próximo argumento, qual é o nome do arquivo do script próprio , deve ser interpretado como um script em vez de como o nome de um arquivo de entrada a ser processado.

Nenhuma regra BEGIN

Você poderia criar um e definir tally = 0 , mas não é necessário porque o AWK permite a aritmética em variáveis não inicializadas e as trata como zero. (Se você estivesse executando gawk --lint -f tally file , talvez queira incluir a atribuição explicitamente para evitar um aviso de "referência a variável não inicializada"). Coloquei uma linha em branco aqui, mas não é necessário.

== "NAME" { printf "%s ", }

Quando o primeiro campo for NAME , imprima o segundo campo como uma sequência ( %s ) seguida por um espaço.

== "PAY" { total += * }

Quando o primeiro campo for PAY , aumente o valor de total pelo produto do segundo e terceiro campos.

== "END" { print total; total = 0 }

Quando o primeiro campo for END , imprima o valor de total . A instrução print anexa automaticamente o separador de registro de saída, que é uma nova linha , pois você não definiu ORS caso contrário . Em seguida, defina total de volta para zero para se preparar para a próxima estrofe (se houver).

    
por Eliah Kagan 26.09.2017 / 06:34