在awk中预填充关联数组键?

在awk中预填充关联数组键?,awk,associative-array,Awk,Associative Array,我已经编写了一个munin插件,它使用slurm的sacct来监视HPC集群上的作业状态。我用sh+awk(而不是我常用的工具perl)编写了它 该脚本可以工作,但我花了很长时间才弄清楚如何预先填充可能状态的关联数组(一些/大部分可能不在SACT输出中,我希望它们默认为零)。谷歌帮不了什么忙,我能想到的最好办法就是对字符串使用split生成一个临时数组,然后我对它进行迭代 我想到了这个: BEGIN { num = split("cancelled completed completin

我已经编写了一个munin插件,它使用slurm的sacct来监视HPC集群上的作业状态。我用sh+awk(而不是我常用的工具perl)编写了它

该脚本可以工作,但我花了很长时间才弄清楚如何预先填充可能状态的关联数组(一些/大部分可能不在SACT输出中,我希望它们默认为零)。谷歌帮不了什么忙,我能想到的最好办法就是对字符串使用split生成一个临时数组,然后我对它进行迭代

我想到了这个:

BEGIN {
    num = split("cancelled completed completing failed nodefail pending running suspended timeout",statenames," ");
    for (i=1;i<=num;i++) {
        states[statenames[i]] = 0
    }
  }
还是这个

%states = map {$_ => 0} qw(cancelled completed completing failed nodefail pending running suspended timeout);
我的问题是:在awk中有没有一种类似于perl版本的方法

[已编辑]

为了澄清这一点,这里有一个sacct输出的示例,我正在将其导入awk。请注意,此输出中的唯一状态是正在运行、已完成和已取消-其他状态不存在(因为它们今天没有出现),但我希望它们仍然存在于我的脚本输出中(以munin可用的“statename.value 0”形式)

[再次编辑]

下面是我的munin插件的示例输出:

# ./slurm-sacct
suspended.value 0
pending.value 0
nodefail.value 0
failed.value 0
running.value 6
completing.value 0
completed.value 5
timeout.value 0
cancelled.value 1

脚本运行并执行我想要的操作,我只是想知道是否有更好的方法初始化关联数组。

您可能根本不需要这样做。awk中的变量是动态的,这意味着它们在第一次使用(分配给或访问)时会自动初始化,这也适用于数组元素

如果在数值上下文中访问变量,则该变量将初始化为0,否则将初始化为空字符串。(至少gawk做到了这一点,尽管我不确定它是否依赖于实现)因此,如果你在做一些事情,比如计算每个州的作业数量,整个程序就像这样简单

{ states[$1]++ }
END {
     for (state in states) print state, states[state]
}
每次执行表达式
状态[$1]+
,它都会检查是否存在
状态[$1]
,如果不存在,则将其初始化为0


编辑:根据您的评论,我猜您希望为每个可能的状态打印一行,而不管该状态中是否有任何作业。在这种情况下,您需要包括所有可能的状态名,并且没有像Perl中那样的快捷方式。据我所知,你已经找到的是最干净的了。(Awk的设计并没有真正考虑到这种用法)

我建议如下:

{ states[$1]++ }
END {
     split("cancelled completed completing failed nodefail pending running suspended timeout",statenames," ");
     for (state in statenames) print state, states[state]+0
}

我认为在awk中更自然的方法是使用单独的密钥文件。考虑一个文件“代码>键.txt <代码>每行一个键。然后,您可以这样做:

foreach (qw(cancelled completed completing failed nodefail pending running suspended timeout)) {
    $states{$_} = 0;
}
printf "key1\nkey2\nkey2\nkey5" | 
  awk '
    FILENAME == "keys.txt" {
      counts[$0] = 0
      next
    }

    {
      counts[$0]++
    }

    END {
      for (key in counts) {
        print key, counts[key]
      }
    }' keys.txt -
keys.txt
中有五个键,这将产生:

key1 1
key2 2
key3 0
key4 0
key5 1
虽然这里的键是按顺序显示的,但这只是偶然的,不应该依赖

对于特定示例,还可以完全跳过关联数组。相反,您可以使用awk最小限度地处理行,并使用
sort | uniq-c
将计数制成表格。对一个密钥文件使用
join
可以确保所有密钥的存在。

awk比Perl更笨拙(我可以说“不那么简洁”)

你可以这样写(类似于@Michael的答案):

数据管道|
awk'
NR==FNR{statenames[$1]=0;next}
{常规处理}
结束{通常输出}

“也许Craig可以使用以下替代:

print "Timeout states ",states[timeout],".";
这:

在我的情况下,如果awk输入中没有超时状态,第一次打印将给出:

超时状态

而第二个将给出:

超时状态为0


@DavidZaslavsky答案的一个调整可能是按照您在split()行中指定的顺序打印状态。这将是:

{ states[tolower($1)]++ }
END {
     n = split("cancelled completed completing failed nodefail pending running suspended timeout",statenames)
     for (i=1; i<=n; i++) {
         state = statenames[i]
         print state, states[state]+0
     }
}
{states[tolower($1)]++}
结束{
n=split(“取消完成完成完成失败的nodefail挂起运行暂停超时”,statenames)

对于(i=1;iyes,这都是真的,但正如我所说的,并非所有的状态都会出现在sacct的每次执行中-我希望它们默认为零,这就是为什么我想用所有可能的状态名称预先填充关联数组键的原因。例如,通常,将出现的唯一状态将是“正在运行”、“已完成”、“失败”和“已取消”。除非e数组的其他可能状态的值为零(关联数组的键),它们不会出现在输出中。至少,如果没有很多特殊的if/then/else代码,就更简单了,只需使用所有可能的键名初始化assoc数组。POSIX awk也会将变量初始化为0或空字符串。我认为它实际上可以回到原始的awk。好的,听起来就像我用BEGIN和你的一样我提出这个问题的主要原因是我很惊讶谷歌没有提出任何直接相关的东西,所以我想这要么是我错过了一些非常明显的东西,要么就是我对awk采用了一种幼稚的心态。无论如何,我会在ca给它一天左右的时间如果其他人提出了一些好的建议,但我想我最终会接受这个答案,因为你的“…尽可能干净…”评论。不过,我更喜欢我的方法,因为它作为初始化/设置的东西对我来说更具概念意义,所以它属于BEGIN块。无论如何,谢谢。哈希是必要的,因为它是“最干净的”/获取输出中列出的所有作业状态的最简单方法,即使它们并不总是出现在输入中。这就是为什么我没有使用sort | uniq | xargs | sed做一些简单的事情。我没有使用perl,因为我已经多年没有使用awk了,除了简单地从列表文本流中提取字段之外,我认为这是一个很好的操作一些awk实践的portunity。另外,不能说我喜欢外部文件的想法。它为脚本添加了外部依赖性,并且没有做任何事情来消除我提出的“笨拙”之处(IMO,更糟)。你说得对。awk没有提供直接初始化ar的方法
print "Timeout states ",states[timeout],".";
print "Timeout states ",int(states[timeout]),".";
{ states[tolower($1)]++ }
END {
     n = split("cancelled completed completing failed nodefail pending running suspended timeout",statenames)
     for (i=1; i<=n; i++) {
         state = statenames[i]
         print state, states[state]+0
     }
}
{ states[tolower($1)]++ }
END {
     n = split("cancelled completed completing failed nodefail pending running suspended timeout",statenames)
     for (i=1; i<=n; i++) {
         state = statenames[i]
         print state, states[state]+0
         delete states[state]
     }
     for (state in states) {
         print "WARNING: found new state name %s\n",state | "cat>&2"
         print state, states[state]+0
     }
}