度量单位正则表达式操作

时间:2013-03-30 00:48:48

标签: regex perl awk

目的

Linux 上,我试图获得一个代表可用系统内存的最终用户友好字符串。

示例:

Your computer has 4 GB of memory.

成功标准

我认为这些方面最终用户友好(您可能不同意):

  • 1G1.0G更具可读性(1 Vs 1.0

  • 1GB1G更具可读性(GB Vs G

  • 1 GB1GBspace-separated计量单位)更具可读性

  • memoryRAMDDRDDR3(无行话)更具可读性

起点

来自 free procps-ng 实用程序有一个适用于人类的选项:

-h, --human
    Show all output fields automatically scaled to shortest three digit unit
    and display the units of print out.  Following units are used.
        B = bytes
        K = kilos
        M = megas
        G = gigas
        T = teras
    If unit is missing, and you have petabyte of RAM or swap, the number is
    in terabytes and columns might not be aligned with header.

所以我决定从那里开始:

> free -h
             total       used       free     shared    buffers     cached
Mem:          3.8G       1.4G       2.4G         0B       159M       841M
-/+ buffers/cache:       472M       3.4G
Swap:         4.9G         0B       3.9G

3.8G听起来很有希望所以我现在要做的就是......

必修步骤

  • 过滤包含人类可读字符串的行的输出(即Mem:

  • 从行中间挑出内存总量(即3.8G

  • 解析数量和度量单位(即3.8G

  • 根据我的喜好格式化并显示字符串(例如GGB,...)

我的尝试

free -h | \
  awk  '/^Mem:/{print $2}' | \
    perl -ne '/(\d+(?:\.\d+)?)(B|K|M|G|T)/ && printf "%g %sB\n", $1, $2'

输出:

3.8 GB

期望的解决方案

  • 我更喜欢使用 gawk ,但我不知道如何

  • 使用更好的,甚至规范(如果有的话)解析字符串中的“float”

  • 我不介意“只是公认的大小字母”(B|K|M|G|T)挑剔匹配,即使这会因为引入新尺寸而不必要地破坏匹配< / p>

  • 我使用%g4.0输出为4,这可能是您不同意的,具体取决于您对这些评论的看法:https://unix.stackexchange.com/a/70553/10283

我的问题,总结

  • 您是否可以仅在awk执行上述操作?
  • 我的perl可以写得比这更优雅,保持严格吗?

记住:

I am a beginner robot. Here to learn. :]

我从安迪莱斯特那里学到的东西

总结为了我自己的利益:如果可以的话,巩固学习。

例如, gawk

@F

可以在 perl

中这样写出来
echo foo bar baz | awk '{print $2}'

除非等同于 gawk echo foo bar baz | perl -ane 'print "$F[1]\n";' ,否则我认为我还是喜欢 gawk 更好,但当然要做 perl 中的所有事情,既更清洁又更有效。(有相同的吗?)


编辑:实际上,这证明存在,并且--field-separator就像在 gawk 中一样:

-F

输出:

echo ooxoooxoooo | perl -Fx -ane 'print join "\n", @F'

  • perl 有一个oo ooo oooo 选项,非常棒:将其视为 Python str.rstrip (如果您不是 Python 负责人,请参阅链接)了解-l的有效性,但会重新附加{{1}您自动输出

谢谢,安迪!


3 个答案:

答案 0 :(得分:3)

是的,我确定你只能这样做,但我是一个Perl家伙,所以这就是你如何做Perl-only。

而不是(B|K|M|G|T)使用[BKMGT]

使用Perl的-l自动从输入中删除换行符并将其添加到输出中。

我认为没有任何理由让Awk做一些剥离,Perl做其余的事情。您可以使用Perl的-a。

对字段进行自动分割

我不知道free -h的输出是什么(我的free没有-h选项)所以我猜这个

free -h | \
perl -alne'/^Mem:/ && ($F[1]=~/(\d+(?:\.\d+)?)[BKMGT]/) && printf( "%g %sB", $1, $2)'

答案 1 :(得分:2)

awk (实际上是 gawk )解决方案

free -h | awk 'FNR == 2 {if (match($2,"[BKMGT]$",a)) r=sprintf("%.0f %sB",substr($2,0,RSTART-1), a[0]); else r=$2 " B";print "Your computer has " r " of memory."}'

或细分以便于阅读

free -h | awk 'FNR == 2 {if (match($2,"[BKMGT]$",a)) r=sprintf("%.0f %sB",
          substr($2,0,RSTART-1), a[0]); else r=$2 " B";
          print "Your computer has " r " of memory."}'

其中

  • FNR是n th 行(如果2执行{}命令)
  • $2是2 nd 字段
  • if(条件)命令; else命令;
  • match(字符串,正则表达式,匹配数组)。正则表达式说&#34;必须以BKMGT&#34;
  • 之一结束
  • r=sprintf将变量r设置为 sprintf %.0f为无小数点浮动
  • RSTART告知比赛发生的位置,a[0]是第一场比赛

输出上面的例子

Your computer has 4 GB of memory.

答案 2 :(得分:0)

另一个冗长的Perl答案:

free -b | 
perl -lane 'if(/Mem/){ @u=("B","KB","MB","GB"); $F[2]/=1024, shift @u while ($F[2]>1024); printf("%.2f %s", $F[2],$u[0])}'