Como usar printf com awk para designar a saída do comando columns separated

2

Eu quero extrair o nome do fabricante, a versão de firmware do cartão e status da saída de fcinfo hba-port , que se parece com:

root-#> fcinfo hba-port
HBA Port WWN: 10000000c96a53c5
        OS Device Name: /dev/cfg/c2
        Manufacturer: Emulex                                                            ←
        Model: LPe11000-M4
        Firmware Version: 2.82a4 (Z3D2.82A4)                                            ←
        FCode/BIOS Version: Boot:5.02a1 Fcode:1.50a9
        Serial Number: VM73059524
        Driver Name: emlxs
        Driver Version: 2.60k (2011.03.24.16.45)
        Type: N-port
        State: online                                                                   ←
        Supported Speeds: 1Gb 2Gb 4Gb
        Current Speed: 4Gb
        Node WWN: 20000000c96a53c5

Eu extraio esses três campos com awk como abaixo ...

root-#> fcinfo hba-port | awk '/Manufacturer:/{m=$2}/Firmware Version:/{F=$3}/State/{print m, F, $2}'
Emulex 2.82a4 online
Emulex 2.82a4 online
Emulex 2.82a4 offline
Emulex 2.82a4 offline
Emulex 2.82a4 online
Emulex 2.82a4 online

Eu quero apresentar a saída de uma forma mais amigável; por exemplo, como abaixo ...

HBA_Manufacturer    Firmware_Version   State
--------------------------------------------
Emulex               2.82a4           online
Emulex               2.82a4           offline
Emulex               2.82a4           offline

... no qual os cabeçalhos são adicionados e alinhados com os dados. Como posso fazer isso?

Por favor, note que eu preciso de uma solução Solaris. (Muitos comandos que funcionam no Linux não funcionam no Solaris.)

    
por krock1516 02.03.2016 / 05:24

4 respostas

4

Você pode alinhar as colunas sem usar o comando column , inteiramente dentro de awk , fazendo

# fcinfo hba-port | awk '
                        BEGIN{man[-1]="HBA_Manufacturer"
                              ver[-1]="Firmware_Version"
                              sta[-1]="State"
                              man[0]="----------------"
                              ver[0]="----------------"
                              sta[0]="-----"
                              i=1
                             }
                        /Manufacturer:/     {man[i]=$2}
                        /Firmware Version:/ {ver[i]=$3}
                        /State:/            {sta[i]=$2; i++}
                        END {
                             maxlen1 = maxlen2 = maxlen3 = 0
                             for (j=-1; j<i; j++) {
                                 if (length(man[j]) > maxlen1) maxlen1 = length(man[j])
                                 if (length(ver[j]) > maxlen2) maxlen2 = length(ver[j])
                                 if (length(sta[j]) > maxlen3) maxlen3 = length(sta[j])
                             }
                             for (j=-1; j<i; j++) {
                                 printf("%-*s  %-*s  %-*s\n", maxlen1, man[j],
                                                              maxlen2, ver[j],
                                                              maxlen3, sta[j])
                             }
                            }'

Isto lê todo o texto de entrada, armazenando os dados (incluindo os cabeçalhos) nos arrays man , ver e sta (fabricante, versão e estado). Os títulos das colunas são colocados nas entradas [-1] e os traços (que formam uma linha entre os títulos e os dados) são colocados nas entradas [0] ; estas se tornam as duas primeiras linhas da saída (veja abaixo). Os dados reais começam em [1] .

Quando chega ao fim dos dados, determina o tamanho máximo dos dados (incluindo os títulos) em cada coluna e, em seguida, imprime todos os dados (das matrizes), usando as larguras das colunas calculadas.

  • printf("%16s", "Emulex") imprime           Emulex (dez espaços em branco iniciais, seguidos pelo nome de seis caracteres, para um total de 16 caracteres).
  • printf("%-16s", "Emulex") (observe o - ) imprime Emulex           (o nome de seis caracteres, seguido por dez espaços em branco à direita, para um total de 16 caracteres).
  • printf("%-*s", 16, "Emulex") faz o mesmo que %código%, exceto que recebe o printf("%-16s", "Emulex") da lista de argumentos em vez da string de formatação.
  • Então, o acima irá produzir saída como

    HBA_Manufacturer  Firmware_Version  State
    ----------------  ----------------  -----
    Emulex            2.82a4            online
    Emulex            2.82a4            online
    Emulex            2.82a4            offline
    Emulex            2.82a4            offline
    Emulex            2.82a4            online
    Emulex            2.82a4            online
    

    Se você quiser mais espaço entre as colunas, adicione espaços ao formato 16 . Por exemplo, printf lhe daria algo mais próximo da saída do exemplo que você mostra.

  • A saída do exemplo que você mostra apresenta uma linha contínua de traços depois dos cabeçalhos. Meu comando, como mostrado acima, dará apenas uma pequena linha de traços sob cada cabeçalho. Isso se torna ainda mais óbvio se alguns dos seus dados forem mais longos:

    HBA_Manufacturer         Firmware_Version  State
    ----------------         ----------------  -----
    Emulex                   2.82a4            online
    Emulex                   2.82a4            online
    Some_other_manufacturer  2.82a4            offline
    Emulex                   2.82a4            offline
    Emulex                   2.82a4            online
    Emulex                   2.82a4            online
    

    Isso pode ser corrigido, se necessário.

  • Isso pode falhar se seus dados de entrada forem enormes, porque "%-*s %-*s %-*s\n" pode ficar sem espaço para armazenar todos os dados.
  • Isso pode falhar com um erro de sintaxe se você tiver uma versão mais antiga de awk (por exemplo, no Solaris). Se isso acontecer, tente colocar a instrução awk em uma linha, como
        printf("%-*s %-*s %-*s\n", maxlen1, man[j], maxlen2, ver[j], maxlen3, sta[j])
por 02.03.2016 / 08:42
2

Se a largura desejada para cada coluna for pré-determinada, awk has printf que funciona muito como em C; aqui se você quiser, por exemplo, colunas de 20 caracteres, 15 caracteres e 10 caracteres com 2 espaços entre:

(as in Q)  /State/{printf "%-20s  %-15s  %-10s\n", m, F, $2}

(O - é alinhado à esquerda; o padrão é alinhado à direita, geralmente bom para números, mas não para texto.)

Se você quiser que as larguras se adaptem aos dados, você PODERIA armazenar os dados em matrizes, calcular as larguras desejadas e usar essas larguras com asterisco como printf "%-*s ", len, data - mas como o G-Man respondeu ao programa column faz isso mais facilmente.

    
por 02.03.2016 / 08:28
1

Você pode alinhar as colunas fazendo

# fcinfo hba-port | awk '
                        BEGIN {printf("HBA_Manufacturer Firmware_Version State\n")
                               printf("---------------- ---------------- -----\n")
                              }
                        /Manufacturer:/ {m=$2}
                        /Firmware Version:/ {F=$3}
                        /State/ {print m, F, $2}' | column -t

Isso produzirá resultados como

HBA_Manufacturer  Firmware_Version  State
----------------  ----------------  -----
Emulex            2.82a4            online
Emulex            2.82a4            online
Emulex            2.82a4            offline
Emulex            2.82a4            offline
Emulex            2.82a4            online
Emulex            2.82a4            online

Notas:

  • Se você quiser mais espaço entre as colunas, use o --output-separator string (ou -o string ) opção no final, para especificar uma string para colocar entre colunas. O padrão é dois espaços; por exemplo, … | column -t -o " " (com quatro espaços) daria algo mais perto para o exemplo de saída que você mostra.
  • A saída do exemplo que você mostra apresenta uma linha contínua de traços depois dos cabeçalhos. Meu comando, como mostrado acima, dará apenas uma pequena linha de traços sob cada cabeçalho. Isso se torna ainda mais óbvio se alguns dos seus dados forem mais longos:

    HBA_Manufacturer         Firmware_Version  State
    ----------------         ----------------  -----
    Emulex                   2.82a4            online
    Emulex                   2.82a4            online
    Some_other_manufacturer  2.82a4            offline
    Emulex                   2.82a4            offline
    Emulex                   2.82a4            online
    Emulex                   2.82a4            online
    

    Isso pode ser corrigido, se necessário.

por 02.03.2016 / 06:46
0

você pode ter que ajustar as guias com tentativa e erro, mas aproximadamente -

awk 'BEGIN{printf("HBA_Manufacturer\t\t\tFirmware_Version\t\t\tState\n----------------------------------------\n")} {... printf("%s\t\t\t%s\t\t\t%s\n", m, f,$2) }
    
por 02.03.2016 / 05:49