将人类可读的文本转换为字节(bytes)在bash中

25

因此我正在尝试在Linux中分析非常大的日志文件,我已经看到了许多相反的解决方案,但记录数据的程序不允许输出格式化,因此它只以人类可读格式输出(我知道,真是太痛苦了)。所以问题是:如何使用awk之类的工具将人类可读转换为字节:

因此将这个内容转换为:

937
1.43K
120.3M

致:

937
1464
126143693

我能承受并期望一些舍入误差。

提前致谢。

附言:只要能提供内联转换,不必使用awk。

我找到了这个,但是给出的awk命令似乎不能正常工作。它输出类似于534K“0”的东西。

我也找到了一个使用sed和bc的解决方案,但由于它使用了bc,所以有效性有限,意味着它每次只能使用一列,并且所有数据都必须适合于bc,否则它会失败。

sed -e 's/K/\*1024/g' -e 's/M/\*1048576/g' -e 's/G/\*1073741824/g' | bc


1
请查看此答案:https://dev59.com/1VLTa4cB1Zd3GeqPbIja - amdn
@amdn,谢谢,我实际上找到了类似的东西并进行了编辑。那个解决方案唯一的问题就是它使用了bc,所以它不能很好地分析整个日志文件。它只能处理数据单列,而且这些数据必须都是相同类型的。 - Devon
在那个答案的底部有一个不使用bc的“一行代码”。 - amdn
5个回答

40

使用GNU Coreutils中的numfmt --from=iec命令。


1
最佳答案 - 张馆长
这可能是最好的答案,但遗憾的是numfmt无法处理浮点格式的输入:例如,numfmt --to iec 1.43K将会给你numfmt: invalid suffix in input: ‘1.43K’。(在macOS 11.6.4 20G417 x86_64上测试了coreutils 9.0)。 - FK82
你是否在使用 --to 时出现了错误,应该使用 --from - brablc
1
注意:如果你要将Gib、Mib等单位转换成--from=iec-i(例如1.62Gi),请注意。 - undefined

20
以下是一种可以理解二进制和十进制前缀的函数,如果需要可以轻松扩展到大单位:

dehumanise() {
  for v in "${@:-$(</dev/stdin)}"
  do  
    echo $v | awk \
      'BEGIN{IGNORECASE = 1}
       function printpower(n,b,p) {printf "%u\n", n*b^p; next}
       /[0-9]$/{print $1;next};
       /K(iB)?$/{printpower($1,  2, 10)};
       /M(iB)?$/{printpower($1,  2, 20)};
       /G(iB)?$/{printpower($1,  2, 30)};
       /T(iB)?$/{printpower($1,  2, 40)};
       /KB$/{    printpower($1, 10,  3)};
       /MB$/{    printpower($1, 10,  6)};
       /GB$/{    printpower($1, 10,  9)};
       /TB$/{    printpower($1, 10, 12)}'
  done
} 

例子:

$ dehumanise 2K 2k 2KiB 2KB 
2048
2048
2048
2000

$ dehumanise 2G 2g 2GiB 2GB 
2147483648
2147483648
2147483648
2000000000

后缀是不区分大小写的。

这个转换为 JavaScript:https://gist.github.com/lanqy/5193417#gistcomment-3253220 - Zack Burt

7

存在Python工具

$pip install humanfriendly  # Also available as a --user install in ~/.local/bin

$humanfriendly --parse-size="2 KB"
2000
$humanfriendly --parse-size="2 KiB"
2048

7
$ cat dehumanise 
937
1.43K
120.3M

$ awk '/[0-9]$/{print $1;next};/[mM]$/{printf "%u\n", $1*(1024*1024);next};/[kK]$/{printf "%u\n", $1*1024;next}' dehumanise
937
1464
126143692

谢谢!这个方法也可以只在一个列上使用,但是它似乎比使用bc方法更可靠。 - Devon
@Devon:嘿,提供一些实际数据,也许你会得到一个实际的解决方案? :) - tink
1
我接受了这个方案,因为它运行良好。在接受之前,我必须进一步测试它。我所要做的就是在前面添加 awk {'print $2'} |(根据列数)以便处理不同的列,在我的分析中,逐列分析可以很好地工作。 - Devon
}后面不需要加;。最后一个next也不需要,因为它已经在代码的末尾了。其他的 next 对于这个简单的代码也可以被删除,因此这样做就可以了:awk '/[0-9]$/{print $1} /[mM]$/{printf "%u\n", $1*(1024*1024)} /[kK]$/{printf "%u\n", $1*1024}' file - Jotne
这个不能处理千兆字节。 - DustWolf

1
这是对@starfry答案的修改。

awk 'function pp(p){printf "%u\n",$0*1024^p} /[0-9]$/{print $0}/K$/{pp(1)}/M$/{pp(2)}/G$/{pp(3)}/T$/{pp(4)}/[^0-9KMGT]$/{print 0}'


让我们来解释一下:

function pp(p) { printf "%u\n", $0 * 1024^p }

定义一个名为pp的函数,它接受一个参数p,并打印$0乘以1024的p次方%u将打印该数字的无符号十进制整数。

/[0-9]$/ { print $0 }

匹配以数字结尾的行($匹配行尾),然后运行{}中的代码。 打印整行($0

/K$/ { pp(1) }

匹配以大写字母K结尾的行,调用函数pp()并将1作为参数传递给它(p == 1)。注意:当$0(例如“1.43K”)在数学方程中使用时,仅使用开头的数字(即“1.43”)。示例中$0 =“1.43K”。
$0 * 1024^p == 1.43K * 1024^1 == 1.43K * 1024 = 1.43 * 1024 = 1464.32

/M$/ { pp(2) }

匹配以大写字母 M 结尾的行,调用函数 pp() 并将 2 传递给它(p == 2)。例如,当 $0 == "120.3M" 时。

$0 * 1024^p == 120.3M * 1024^2 == 120.3M * 1024^2 == 120.3M * 1024*1024 = 120.3 * 1048576 = 126143692.8

等等... for GT

/ [^ 0-9KMGT] $ / {打印0}

以数字或大写字母K、M、G或T结尾的行将打印"0"。


Example:

$ cat dehumanise
937
1.43K
120.3M
5G
933G
12.2T
bad
<>

结果:

$ awk 'function pp(p){printf "%u\n",$0*1024^p} /[0-9]$/{print $0}/K$/{pp(1)}/M$/{pp(2)}/G$/{pp(3)}/T$/{pp(4)}/[^0-9KMGT]$/{print 0}' dehumanise
937
1464
126143692
5368709120
1001801121792
13414041858867
0
0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接