linux - 使用 awk 从文件中读取元组

标签 linux bash shell awk

您好,我需要一个脚本来使用 awk 从/proc/interrupts 文件中读取 eth 中断的数量,并找到每个 CPU 核心的中断总数。然后我想在 bash 中使用它们。文件的内容是;

      CPU0       CPU1       CPU2       CPU3

47:   33568      45958      46028      49191     PCI-MSI-edge    eth0-rx-0
48:     0          0          0          0       PCI-MSI-edge      eth0-tx-0
49:     1          0          1          0       PCI-MSI-edge      eth0
50:   28217      42237      65203      39086     PCI-MSI-edge    eth1-rx-0
51:     0          0          0          0       PCI-MSI-edge      eth1-tx-0
52:     0          1          0          1       PCI-MSI-edge      eth1
59:     114991     338765      77952     134850  PCI-MSI-edge eth4-rx-0
60:     429029     315813     710091      26714  PCI-MSI-edge eth4-tx-0
61:      5          2          1          5      PCI-MSI-edge     eth4
62:    1647083     208840    1164288     933967  PCI-MSI-edge eth5-rx-0
63:     673787    1542662     195326    1329903  PCI-MSI-edge eth5-tx-0
64:     5          6          7          4       PCI-MSI-edge      eth5

我正在这段代码中用 awk 读取这个文件:

#!/bin/bash

 FILE="/proc/interrupts"

 output=$(awk 'NR==1 {
 core_count = NF 
 print core_count
 next
}
/eth/ {
 for (i = 2; i <= 2+core_count; i++)
 totals[i-2] += $i
}

END {
 for (i = 0; i < core_count; i++)
 printf("%d\n", totals[i])
}
' $FILE)

core_count=$(echo $output | cut -d' ' -f1)

output=$(echo $output | sed 's/^[0-9]*//')

totals=(${output// / })

在这种方法中,我处理核心总数,然后处理每个核心的总中断数,以便在我的脚本中对它们进行排序。但我只能像这样处理总数数组中的数字,

    totals[0]=22222
    totals[1]=33333

但我需要将它们作为带有 CPU 内核名称的元组来处理。

    totals[0]=(cPU1,2222)
    totals[1]=(CPU',3333)

我认为我必须将名称分配给一个数组,然后在我的 SED 中将它们作为元组读取到 bash。我怎样才能做到这一点?

最佳答案

首先,bash 中没有“元组”这样的东西。阵列是完全平坦的。这意味着您要么拥有“标量”变量,要么拥有一级标量数组。

您面临的任务有多种方法。要么:

  1. 如果您使用的是足够新的 bash (4.2 AFAIR),则可以使用关联数组(散列、映射或您如何调用它)。然后,CPU 名称将是键,数字将是值;
  2. 创建一个普通数组(类似 perl 的哈希),其中奇数索引将包含键(CPU 名称),偶数索引将包含值。
  3. 创建两个单独的数组,一个包含 CPU 名称,另一个包含值,
  4. 只创建一个数组,CPU 名称与值之间用一些符号分隔(即 =:)。

让我们先介绍方法 2:

#!/bin/bash

FILE="/proc/interrupts"

output=$(awk 'NR==1 {
    core_count = NF
    for (i = 1; i <= core_count; i++)
        names[i-1] = $i
    next
}
/eth/ {
    for (i = 2; i <= 2+core_count; i++)
        totals[i-2] += $i
}

END {
    for (i = 0; i < core_count; i++)
        printf("%s %d\n", names[i], totals[i])
}
' ${FILE})

core_count=$(echo "${output}" | wc -l)
totals=(${output})

请注意我为简化脚本所做的一些更改:

  1. awk 现在输出“cpu-name number”,每行一个,由一个空格分隔;
  2. 核心计数不是由 awk 输出(以避免预处理输出),而是从输出中的行数推导出来,
  3. totals 数组是通过展平输出创建的——空格和换行符都将被视为空格并用于分隔值。

生成的数组如下所示:

totals=( CPU0 12345 CPU1 23456 ) # ...

要遍历它,您可以使用类似(简单的方法):

set -- "${totals[@}}"
while [[ $# -gt 0 ]]; do
    cpuname=${1}
    value=${2}

    # ...

    shift;shift
done

现在让我们为方法 1 修改它:

#!/bin/bash

FILE="/proc/interrupts"

output=$(awk 'NR==1 {
    core_count = NF
    for (i = 1; i <= core_count; i++)
        names[i-1] = $i
    next
}
/eth/ {
    for (i = 2; i <= 2+core_count; i++)
        totals[i-2] += $i
}

END {
    for (i = 0; i < core_count; i++)
        printf("[%s]=%d\n", names[i], totals[i])
}
' ${FILE})

core_count=$(echo "${output}" | wc -l)
declare -A totals
eval totals=( ${output} )

注意:

  1. awk 输出格式已更改以适应关联数组语义,
  2. totals 声明为关联数组(declare -A),
  3. 遗憾的是,必须使用 eval 让 bash 直接处理输出。

生成的数组如下所示:

declare -A totals=( [CPU0]=12345 [CPU1]=23456 )

现在您可以使用:

echo ${totals[CPU0]}

for cpu in "${!totals[@]}"; do
    echo "For CPU ${cpu}: ${totals[${cpu}]}"
done

第三种方法可以通过多种不同的方式完成。假设您可以允许两次读取 /proc/interrupts,您甚至可以这样做:

FILE="/proc/interrupts"

output=$(awk 'NR==1 {
    core_count = NF
    next
}
/eth/ {
    for (i = 2; i <= 2+core_count; i++)
        totals[i-2] += $i
}

END {
    for (i = 0; i < core_count; i++)
        printf("%d\n", totals[i])
}
' ${FILE})

core_count=$(echo "${output}" | wc -l)
names=( $(cat /proc/interrupts | head -n 1) )
totals=( ${output} )

所以,现在 awk 再次只输出计数,名称由 bash 直接从 /proc/interrupts 的第一行获取。或者,您可以从方法 (2) 中获得的单个数组创建拆分数组,或以其他方式解析 awk 输出。

结果将在两个数组中:

names=( CPU0 CPU1 )
totals=( 12345 23456 )

并输出:

for (( i = 0; i < core_count; i++ )); do
    echo "${names[$i]} -> ${totals[$i]}"
done

最后一种方法:

#!/bin/bash

FILE="/proc/interrupts"

output=$(awk 'NR==1 {
    core_count = NF
    for (i = 1; i <= core_count; i++)
        names[i-1] = $i
    next
}
/eth/ {
    for (i = 2; i <= 2+core_count; i++)
        totals[i-2] += $i
}

END {
    for (i = 0; i < core_count; i++)
        printf("%s=%d\n", names[i], totals[i])
}
' ${FILE})

core_count=$(echo "${output}" | wc -l)
totals=( ${output} )

现在(常规)数组如下所示:

totals=( CPU0=12345 CPU1=23456 )

你可以这样解析它:

for x in "${totals[@]}"; do
    name=${x%=*}
    value=${x#*=}
    echo "${name} -> ${value}"
done

(现在请注意,拆分 CPU 名称和值发生在循环中)。

关于linux - 使用 awk 从文件中读取元组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11608323/

相关文章:

linux - HDFS 和 linux 文件大小

linux - 如何在 Linux 中将 "tee"与 "source"命令一起使用?

bash - 使用脚本查看所有文件夹和子文件夹

linux - shell 脚本 : Using grep and tr in the shebang

shell - vagrant puppet 配置的功能测试(或运行 CLI 测试套件)

linux - 在 Linux 中使用 SSL 搜索文件

linux - 如何测试两条路径是否可硬链接(hard link)?

Linux shell : why zero ending line dont work as expected?

linux - Shell 的交错脚本

c - 除 gcc 外,我可以使用哪个程序在 Linux 中进行调试?