方便解析带有单位后缀的数字吗?

方便解析带有单位后缀的数字吗?

假设您拥有以人性化格式存储数量的数据,例如 的输出du -h,并且想要进一步操作这些数字。假设您想通过 grep 管道传输数据,以对该数据的子集进行求和。您会在许多从未见过的系统上临时执行此操作,并且只有最低限度的实用程序。您希望对所有标准的 10^n 后缀进行后缀转换。

是否存在一个 gnu-linux 实用程序,用于在管道内将后缀数字转换为实数?您是否编写了一个 bash 函数来执行此操作,或者编写了一些容易记住的 perl,而不是一长串正则表达式替换或几个 sed 步骤?

38M     /var/crazyface/courses/200909-90147
2.7M    /var/crazyface/courses/200909-90157
1.1M    /var/crazyface/courses/200909-90159
385M    /var/crazyface/courses/200909-90161
1.3M    /var/crazyface/courses/200909-90169
376M    /var/crazyface/courses/200907-90171
8.0K    /var/crazyface/courses/200907-90173
668K    /var/crazyface/courses/200907-90175
564M    /var/crazyface/courses/200907-90178
4.0K    /var/crazyface/courses/200907-90179

| grep 200907 | <amazing suffix conversion> | awk '{s+=$1} END {print s}'


相关参考:

答案1

根据我对您链接到的一个问题的回答:

awk '{
    ex = index("KMGTPEZY", substr($1, length($1)))
    val = substr($1, 0, length($1) - 1)

    prod = val * 10^(ex * 3)

    sum += prod
}
END {print sum}'

使用的另一种方法:

sed 's/G/ * 1000 M/;s/M/ * 1000 K/;s/K/ * 1000/; s/$/ +\\/; $a0' | bc

答案2

就我个人而言,我根本不会使用 -h 标志。“人类可读”版本会舍去数字,而当您转换回来时,这些数字需要再次舍入,因此变得更加不准确。(例如,2.7MiB 是 2831155.2 字节。您对另外 0.8 个字节做了什么?!)

否则,您可以要求units将 MiB/GiB/KiB 转换为“B”,它会处理这个问题,但您必须执行类似操作(假设您的输出是制表符,否则cut适当)

{your output} | cut -f1 '-d{tab}' | xargs -L 1 -I {} units -1t {}iB B | awk '{s+=$1}END{printf "%d\n",s}'

答案3

您可以使用 perl 正则表达式来执行此操作。例如,

$value = 0;
if($line =~ /(\d+\.?\d*)(\D+)\s+/) {
   $amplifier = 1024 if ($2 eq 'K');
   $amplifier = 1024 * 1024 if ($2 eq 'M');
   $amplifier = 1024 * 1024 * 1024 if ($2 eq 'G');
   $value = $1 * $amplifier;
}

这是一个简单的脚本。您可以将其视为起点。希望它能有所帮助!

答案4

尝试numfmtGNU 核心实用程序(核心实用程序)。它负责将“原始”字节单元转换为人类可读的字节单元,或从人类可读的字节单元转换为“原始”字节单元。多字节单元,表示 1024 (IEC) 或 1000 (SI) 的倍数。

既然你提到了du(也来自 coreutils)和du --human-readable,我假设您的样本输入数据是 IEC 格式,四舍五入到最多一位小数。

输入.txt

38M     /var/crazyface/courses/200909-90147
2.7M    /var/crazyface/courses/200909-90157
1.1M    /var/crazyface/courses/200909-90159
385M    /var/crazyface/courses/200909-90161
1.3M    /var/crazyface/courses/200909-90169
376M    /var/crazyface/courses/200907-90171
8.0K    /var/crazyface/courses/200907-90173
668K    /var/crazyface/courses/200907-90175
564M    /var/crazyface/courses/200907-90178
4.0K    /var/crazyface/courses/200907-90179

命令

cat input.txt | numfmt --from 'iec' | tee output.txt

输出.txt

39845888     /var/crazyface/courses/200909-90147
2831156    /var/crazyface/courses/200909-90157
1153434    /var/crazyface/courses/200909-90159
403701760    /var/crazyface/courses/200909-90161
1363149    /var/crazyface/courses/200909-90169
394264576    /var/crazyface/courses/200907-90171
8192    /var/crazyface/courses/200907-90173
684032    /var/crazyface/courses/200907-90175
591396864    /var/crazyface/courses/200907-90178
4096    /var/crazyface/courses/200907-90179

大小字符串长度的变化会弄乱列格式,如果您进一步解析输出,这并不重要。


更多替代方案和建议

在输出端,columnutil-linux可以将其格式化回漂亮的表格:

cat output.txt | column --table

为了获得更好的精度,考虑使用du --bytes得到(累计)字节大小而不是(累积)文件系统块大小。它应该与(累积)文件内容/数据大小相匹配,而(默认)块大小会引入额外的舍入误差。例如,示例集中的最后一行列出了大小,4.0K尽管它可能小于大小,4096 bytes如在转换回字节大小后输出中所示。


使用du --threshold=size方便地根据(累积)大小过滤输出。例如,--threshold '+5M'用于排除“小”条目,或--threshold '-100M'排除“大”条目。可选的du --total参数仍将包括跳过的条目的大小。


使用 GNU Grep 等工具后,可以将numfmt漂亮打印功能du转移到“后处理”grep和“简单”数字sort(来自 coreutils):

du | grep '200907' | sort --numeric-sort | numfmt --to 'iec'

对于基于名称的路径排除,根据您的输入样本,您可以跳过grep并改用du --exclude '200909-*'仅保留200907-*路径。这假设只有一个或几个简单的模式需要过滤掉。


或者使用强大的路径(等等)过滤find首先然后使用xargs(均来自GNU Findutils)将匹配项作为参数应用于du

find . -name '200907-*' | xargs --no-run-if-empty du --human-readable --total

find还可以通过以下方式过滤小/大/精确文件大小find -size,但要小心包含大小后缀(例如c字节)。可选的du --total如果条目超出参数限制,可能会重复(在批次之间)。


在 shell 命令之间传输路径时,可能值得使用\0/ NUL/ null/ 以零结尾的数据,而不是默认的换行符\n。查看诸如--null/ --zero-terminated/之类的参数-print0,以避免(某些)输入/输出路径可能包含意外“特殊”字符的问题。

相关内容