2

I want to extract the manufacturer name, card's firmware version and status from the output of fcinfo hba-port, which looks like:

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

I extract those three fields with awk as below...

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

I want to present the output in a more user-friendly form; for example, like below...

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

... in which headers are added and lined up with the data.  How can I do this?

Please note that I need a Solaris solution.  (Many commands that work on Linux don't work on Solaris.)

krock1516
  • 343

4 Answers4

4

You can get the columns to line up, without using the column command, entirely within awk, by doing

# 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])
                             }
                            }'

This reads the entire input text, storing the data (including the headers) in the man, ver, and sta (manufacturer, version, and state) arrays.  The column headings are placed in the [-1] entries, and the dashes (which form a line between the headings and the data) are placed in the [0] entries; these become the first two lines of the output (see below).  The actual data start at [1].

When it gets to the end of the data, it determines the maximum length of the data (including the headings) in each column, and then prints all the data (from the arrays), using the calculated column widths.

  • printf("%16s", "Emulex") prints           Emulex (ten initial blanks, followed by the six character name, for a total of 16 characters).
  • printf("%-16s", "Emulex") (note the -) prints Emulex           (the six character name, followed by ten trailing blanks, for a total of 16 characters).
  • printf("%-*s", 16, "Emulex") does the same as printf("%-16s", "Emulex"), except it gets the 16 from the argument list instead of the format string.
  • So the above will produce output like

    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
    

    If you want more space between the columns, add spaces to the printf format.  For example, "%-*s %-*s %-*s\n" would give you something closer to the example output you show.

  • The example output you show features a continuous row of dashes after the headers.  My command, as shown above, will give only a short row of dashes under each header.  This becomes even more obvious if some of your data are longer:

    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
    

    This can be fixed, if necessary.

  • This may fail if your input data are huge, because awk might run out of space to hold all the data.
  • This might fail with a syntax error if you have an older version of awk (e.g., on Solaris).  If that happens, try putting the printf statement all on one line, as
          printf("%-*s %-*s %-*s\n", maxlen1, man[j], maxlen2, ver[j], maxlen3, sta[j])
    2

    If the width you want for each column is predetermined, awk has printf that works much like in C; here if you want for example columns of 20 chars, 15 chars, and 10 chars with 2 spaces between:

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

    (The - gives left-aligned; the default is right-aligned, usually good for numbers but not text.)

    If you want the widths to adapt to the data, you COULD store the data in arrays, compute the desired widths, and then use those widths with asterisk like printf "%-*s ", len, data -- but as G-Man answered the column program does that more easily.

    1

    You can get the columns to line up by doing

    # 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
    

    This will produce output like

    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
    

    Notes:

    • If you want more space between the columns, use the --output-separator string (or -o string) option at the end to specify a string to put between columns. The default is two spaces; for example, … | column -t -o " " (with four spaces) would give you something closer to the example output you show.
    • The example output you show features a continuous row of dashes after the headers.  My command, as shown above, will give only a short row of dashes under each header.  This becomes even more obvious if some of your data are longer:

      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
      

      This can be fixed, if necessary.

    • column command doesn't work on Solaris Host though its pretty resilient in Linux Systems. But the Header information is printed via your code....but columns can not be lined up :( – krock1516 Mar 02 '16 at 05:59
    • I have placed the tab in between print /State/ {print m"\t\t", F"\t\t", $2}' for the better column line up until any better to best trick. – krock1516 Mar 02 '16 at 06:32
    0

    you may have to adjust the tabs with trial and error, but roughly --

    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) }
    
    jai_s
    • 1,500
    • : i just edited my post , in the first line i have got the Output i require but now i want to have Header Name to we placed with printf.... which i am unable to get.... – krock1516 Mar 02 '16 at 05:21