将 awk printf 字符串转换为十进制

将 awk printf 字符串转换为十进制

任务:

stdout 以十进制形式从顶部输出平均负载(例如 0.23)

细节:

我需要将此脚本解析为 Chef 的 inspec 并检查其结果是否大于/小于某些内容,例如:

describe command("top -b -n  1  | awk  '/load average/ { sub(/,/,\".\",$10); printf \"%f\n\",$10}'") do
  its("stdout") { should eq 0.00 }    
end

此示例返回“”

但现在当我想到这一点时,我可以与中的文件进行比较/proc/loadavg

进步

使用此资源:用top获取平均负载

通过这个命令,我得到了输出的良好表示,但它是一个字符串,我无法用它进行任何数学运算:

martin@martinv-pc:~$ top -b -n  1  | awk '/load average/ { printf "%s\n", $10}'
0,63,

但是当我尝试将 printf 更改为十进制/浮点时,出现错误:

martin@martinv-pc:~$ top -b -n  1  | awk '/load average/ { printf "%f\n", $10}'
0.000000
martin@martinv-pc:~$ top -b -n  1  | awk '/load average/ { printf "%d\n", $10}'
0

无法回声,尝试剪切,馊主意-- 不工作:

martin@martinv-pc:~$ top -b -n  1  | awk '/load average/ { printf "%s\n", $10}'|cut -c1-4
0,15
martin@martinv-pc:~$ top -b -n  1  | awk '/load average/ { printf "%s\n", $10}'|$((cut -c1-4))
-4: command not found

另一种尝试:

martin@martinv-pc:~$ top -b -n  1  | awk '/load average/ BEGIN { printf "%.f\n", $10};'
awk: line 1: syntax error at or near BEGIN

问题:

如何将字符串值转换为小数/浮点/整数?

ps -o user,rss输出:

[vagrant@localhost ~]$ ps -o user,rss
USER       RSS
vagrant    736
vagrant   1080

答案1

gawk:对用逗号分隔符格式化的浮点数求和

答案是使用--use-lc-numericgawk 选项。

--使用-lc-数字

这会强制 gawk 在解析输入数据时使用语言环境的小数点字符。尽管 POSIX 标准要求这种行为,并且 gawk 在 --posix 生效时也会这样做,但默认情况下是遵循传统行为并使用句点作为小数点,即使在句点不是小数点字符的语言环境中也是如此。此选项会覆盖默认行为,而没有 --posix 选项的完全严格的严格性。

在你的情况下,这个命令应该有效:

top -b -n 1 | awk --use-lc-numeric '/load average/ { printf "%f\n", $10}'

答案2

,您可能正在使用具有小数点分隔符的区域设置。您可以尝试以下任何一种方法:

  1. 使用 C 语言环境top

    LC_ALL=C top -b -n  1  | awk  '/load average/ { printf "%f\n",$10}'
    

    这不仅解决了句号与逗号的问题,而且还避免了文本问题,例如load average翻译成用户语言的问题。

  2. 将逗号替换为点:

    top -b -n  1  | awk  '/load average/ { sub(/,/,".",$10); printf "%f\n",$10}'
    
  3. 对于 GNU awk,使用--use-lc-numeric标志由@Leo推荐或使用POSIXLY_CORRECT=1 awk.

  4. 或者使用 POSIX 兼容的awk实现,例如真正的 awk 默认情况下,它应该根据区域设置规则解析和打印数字。

请注意,获取平均负载的更便携的命令是uptime

答案3

任务:

sdout 以十进制形式从顶部开始的平均负载(例如 0.23)

解决方案:

top -b -n  1  | perl -lane 'print "$1.$2" if /load average: (\d+)[,.](\d+)/'

笔记:

这将检索 1m 负载平均值。看起来这就是你想要的,但你应该说清楚。如果需要,可以轻松修改代码以检索 5 分钟或 15 分钟甚至全部三分钟内的负载平均值。

正如所指出的@特登,可能是比本例uptime更好的起点。top

前两行之后,您隐晦地描述你想对结果做什么。您想要采取的后续步骤应该成为新问题的主题。

在 Perl 中,数字会自动转换为字符串,反之亦然。可以对表示数字的字符串执行任何数值运算。例如print "$1.$2"+11.11


问题2:

这一部分是关于第二个问题,即完全不相关到第一个。
我敦促OP单独发布这个问题

如何将字符串值转换为小数/浮点/整数?

更好的写法是:对字符串执行数值比较厨师规格检查

解决方案:

to_i使用或将字符串转换为数字格式to_f

例子:

describe command("echo 1.00") do
    its("stdout.to_f") { should be < 1.03 }
end

解释:

非常合理的是,stdout 被视为字符串。也非常合理,数字比较要求两个数字是...数字。幸运的是,可以使用方便的 Ruby 字符串方法来完成转换:to_ito_f和。to_rto_c

答案4

您的问题是提取的字段使用逗号作为小数点分隔符,而 awk 期望浮点数使用点。

这重现了您的问题:

$ LC_ALL=de_DE top -bn 1 | awk 'NR==1'
top - 08:37:07 up 1 day, 10:22,  5 users,  load average: 0,17, 0,24, 0,26

如您所见,数字使用逗号作为小数点分隔符。你需要类似的东西:

$ LC_ALL=en_US top -bn 1 | awk 'NR==1'
top - 08:38:28 up 1 day, 10:23,  5 users,  load average: 0.56, 0.34, 0.30

但这取决于locale -a您的系统上安装了哪些区域设置(使用 进行检查)。区域C设置始终可用。

$ LC_ALL=C top -bn 1 | awk 'NR==1'
top - 08:40:35 up 1 day, 10:25,  5 users,  load average: 0.50, 0.39, 0.31

但是使用 top 只提取第一行就有点矫枉过正了。更好地利用正常运行时间:

$ LC_ALL=C uptime
08:42:08 up 1 day, 10:27,  5 users,  load average: 0.35, 0.37, 0.31

但更好的是,阅读/proc/loadavg文件

$ cat /proc/loadavg 
0.29 0.34 0.30 1/468 15084

顺便说一下,这不受语言环境的影响。第一个数字是 1 分钟负载平均值,只需选择它并以任何格式打印:

$ awk '{printf( "%s\n %f\n %d\n", $1, $1, $1 )}' /proc/loadavg
0.35
0.350000
0

对于 1、5 和 15 分钟负载平均使用:

$ awk '{printf( "%7.3f %7.3f %5.2f\n", $1, $2, $3 )}' /proc/loadavg
 0.150   0.340  0.33

相关内容