如何将 printf 与 awk 一起使用来指定分隔命令输出的列

如何将 printf 与 awk 一起使用来指定分隔命令输出的列

我想从 的输出中提取制造商名称、卡的固件版本和状态fcinfo hba-port,如下所示:

root-#> fcinfo hba 端口
HBA 端口 WWN:10000000c96a53c5
        操作系统设备名称:/dev/cfg/c2
        制造商: Emulex
        型号:LPe11000-M4
        固件版本:2.82a4(Z3D2.82A4)
        FCode/BIOS 版本:启动:5.02a1 Fcode:1.50a9
        序列号:VM73059524
        驱动程序名称: emlxs
        驱动程序版本:2.60k (2011.03.24.16.45)
        类型:N 端口
        状态:在线
        支持的速度:1Gb 2Gb 4Gb
        当前速度:4Gb
        节点WWN:20000000c96a53c5

我用下面的方法提取这三个字段awk......

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

我想以更用户友好的形式呈现输出;例如,像下面这样...

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

...其中添加标题并与数据对齐。我怎样才能做到这一点?

请注意,我需要 Solaris 解决方案。 (许多适用于 Linux 的命令不适用于 Solaris。)

答案1

您可以在不使用命令的情况下使列column完全在 内对齐awk,方法是

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

这将读取整个输入文本,并将数据(包括标题)存储在manversta(制造商、版本和状态)数组中。列标题放置在[-1]条目中,破折号(在标题和数据之间形成一条线)放置在[0]条目中;这些成为输出的前两行(见下文)。实际数据从 开始[1]

当到达数据末尾时,它确定每列中数据的最大长度(包括标题),然后使用计算出的列宽度打印所有数据(来自数组)。

  • printf("%16s", "Emulex")打印          Emulex (十个开头的空格,后跟六个字符的名称,总共 16 个字符)。
  • printf("%-16s", "Emulex")(注意-) 打印Emulex           (六个字符的名称,后跟十个尾随空格,总共 16 个字符)。
  • printf("%-*s", 16, "Emulex")与 的作用相同 printf("%-16s", "Emulex"),只是它从参数列表中获取 ,16而不是格式字符串。
  • 所以上面会产生类似的输出

    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
    

    如果您希望列之间有更多空间,请在printf格式中添加空格。例如,"%-*s %-*s %-*s\n" 会给您一些更接近您显示的示例输出的内容。

  • 您显示的示例输出在标题后面有一行连续的破折号。如上所示,我的命令只会在每个标题下给出一小行破折号。如果某些数据较长,这一点会变得更加明显:

    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
    

    如有必要,可以修复此问题。

  • 如果您的输入数据很大,这可能会失败,因为awk可能会耗尽空间来容纳所有数据。
  • 如果您有旧版本awk(例如,在 Solaris 上),这可能会失败并出现语法错误。如果发生这种情况,请尝试将printf语句全部放在一行上,如下所示
          printf("%-*s %-*s %-*s\n", maxlen1, man[j], maxlen2, ver[j], maxlen3, sta[j])

答案2

如果您想要的每列宽度是预先确定的,awkprintf其工作原理与 C 语言非常相似;例如,如果您想要 20 个字符、15 个字符和 10 个字符的列(中间有 2 个空格):

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

-给出左对齐;默认为右对齐,通常适用于数字,但不适用于文本。)

如果您希望宽度适应数据,您可以将数据存储在数组中,计算所需的宽度,然后使用带有星号的这些宽度,例如printf "%-*s ", len, data-- 但是G-Man 回答了这个column节目更容易做到这一点。

答案3

您可以通过执行以下操作使列对齐

# 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

这将产生类似的输出

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

笔记:

  • 如果您希望列之间有更多空间,请使用末尾的 (或) 选项来指定要在列之间放置的字符串。默认是两个空格;例如, (有四个空格)会给您一些更接近您显示的示例输出的内容。--output-separator string-o string… | column -t -o " "
  • 您显示的示例输出在标题后面有一行连续的破折号。如上所示,我的命令只会在每个标题下给出一小行破折号。如果某些数据较长,这一点会变得更加明显:

    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
    

    如有必要,可以修复此问题。

答案4

你可能需要通过反复试验来调整选项卡,但大致上——

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) }

相关内容