如何用 shell 迭代 Linux cpuset?

How can a Linux cpuset be iterated over with shell?

Linux 的 sys 文件系统表示 CPU 组 ID,其语法为:

  1. 0,2,8:包含 0、2 和 8 的 CPU 集合。
  2. 4-6:一组 CPU,包含 4、5 和 6。
  3. 两种语法可以混合搭配,例如:0,2,4-6,8

例如,运行 cat /sys/devices/system/cpu/online 在我的机器上打印 0-3 这意味着 CPUs 0、1、2 和 3 在线。

问题是上述语法很难在 shell 脚本中使用 for 循环迭代。如何将上述语法转换为更常规的语法,例如 0 2 4 5 6 8?

eval echo $(cat /sys/devices/system/cpu/online | sed 's/\([[:digit:]]\+\)-\([[:digit:]]\+\)/$(seq  )/g' | tr , ' ')

解释:

  1. cat /sys/devices/system/cpu/online 从 sysfs 读取文件。这可以更改为任何其他文件,例如 offline.
  2. 输出通过替换管道传输 s/\([[:digit:]]\+\)-\([[:digit:]]\+\)/$(seq )/g。这匹配 4-6 之类的内容并将其替换为 $(seq 4 6).
  3. tr , ' ' 将所有逗号替换为空格。
  4. 此时输入0,2,4-6,8被转化为0 2 $(seq 4 6) 8。最后一步就是eval这个序列得到0 2 4 5 6 8.
  5. 示例 echo 的输出。或者,它可以写入变量或在 for 循环中使用。

尝试:

$ echo 0,2,4-6,8 | awk '/-/{for (i=; i<=; i++)printf "%s%s",i,ORS;next} 1' ORS=' ' RS=, FS=-
0 2 4 5 6 8

这可以在循环中使用,如下所示:

for n in $(echo 0,2,4-6,8 | awk '/-/{for (i=; i<=; i++)printf "%s%s",i,ORS;next} 1' RS=, FS=-)
do
   echo cpu="$n"
done

产生输出:

cpu=0
cpu=2
cpu=4
cpu=5
cpu=6
cpu=8

或喜欢:

printf "%s" 0,2,4-6,8 | awk '/-/{for (i=; i<=; i++)printf "%s%s",i,ORS;next} 1' RS=, FS=- | while read n
do
   echo cpu="$n"
done

这也会产生:

cpu=0
cpu=2
cpu=4
cpu=5
cpu=6
cpu=8

工作原理

awk 命令的工作原理如下:

  • RS=,

    这告诉 awk 使用 , 作为记录分隔符。

    例如,如果输入是 0,2,4-6,8,则 awk 将看到四个记录:02 以及 4-68

  • FS=-

    这告诉 awk 使用 - 作为字段分隔符。

    以这种方式设置 FS,例如,如果输入记录由 2-4 组成,则 awk 会将 2 视为第一个字段,而 4作为第二个字段。

  • /-/{for (i=; i<=; i++)printf "%s%s",i,ORS;next}

    对于包含 - 的任何记录,我们打印出从第一个字段的值 </code> 开始到第二个字段的值 <code> 结束的每个数字].每个这样的数字后跟输出记录分隔符 ORS。默认情况下,ORS 是换行符。对于上面的一些示例,我们将 ORS 设置为空白。

    打印完这些数字后,我们跳过其余的命令并跳转到 next 记录。

  • 1

    如果我们到达这里,则记录不包含 -,我们按原样打印出来。 1 是 awk 用于打印行的 shorthand。

一个 Perl:

echo "0,2,4-6,8" | perl -lpe 's/(\d+)-(\d+)/{..}/g; $_="echo {$_}"' | bash

只需将原始字符串转换为echo {0,2,{4..6},8},然后让bash 'brace expansion'进行插值即可。