具有内存大小单位的值的算术

Maë*_*lan 9 shell arithmetic

假设我有一堆表示内存量的数字,例如以“86k或”320m或“或”的形式书写1.7g。如何在命令行中计算它们的总和,并获得人类可读的结果?

能够计算减法也很好。完美的工具将处理多组符号(例如1g/ 1G/ 1GB/ 1Go/ 1GiB/ 1.7Gio)及其含义(二进制或十进制乘数)。

我正在寻找一个纯粹的计算器。这些数字不一定是我磁盘上某些文件的大小,因此诸如findstat或 之du类的工具不是一种选择。

这显然很容易实现(在精度方面有一些障碍),但如果这还不存在,我会被诅咒!

Voj*_*fny 9

一点自我提升:我们编写了一个名为libbytesize的库来使用 C 和 Python 进行这些计算,并且它还有一个名为bscalc的命令行工具

$ bscalc "5 * (100 GiB + 80 MiB) + 2 * (300 GiB + 15 GiB + 800 MiB)"
1215425413120 B
1186938880.00 KiB
   1159120.00 MiB
      1131.95 GiB
         1.11 TiB
Run Code Online (Sandbox Code Playgroud)

该库打包在大多数发行版中,不幸的是该工具不是。它在 Fedoralibbytesize-tools和 SuSE 中bscalc,但不在 Debian/Ubuntu 中。


Sté*_*las 6

在 中zsh,您可以定义一个数学函数,如:

() {
  typeset -gA bsuffix
  local n=1 ni=1 s
  for s (k m g t p e) {
    (( n *= 1000 )); (( ni *= 1024 ))
    (( bsuffix[$s] = bsuffix[${s}ib] = bsuffix[${s}io] = ni ))
    (( bsuffix[${s}b] = bsuffix[${s}o] = n ))
  }
}
b() {
  set -o localoptions -o extendedglob
  local s=${(M)1%(#i)(${(j:|:k)~bsuffix})}
  (( ${1%$s} * ${bsuffix[$s:l]-1} ))
}

functions -Ms b
Run Code Online (Sandbox Code Playgroud)

然后,你可以使用b(1G)b(1mB)在任何zsh的算术表达式,像(( .... ))$(( ... ))$array[...],等,或在zcalc

$ <<< $((b(86k) + b(320mb) + b(1.7gio)))
2145449164.8

$ autoload zcalc
$ zcalc
1> b(86k) + b(320mb) + b(1.7gio)
2.14545e+09
2> :sci 15
2145449164.8

$ echo $(( b(infeo) ))
Inf   
Run Code Online (Sandbox Code Playgroud)

(请注意,我们在bB(或o/ O)之间没有区别,匹配不区分大小写。它不被解释为bitbyte)。

另一种方法可能是让b()函数将整个表达式作为参数,并将所有后缀替换为* $bsuffix[<suffix>]

b() {
  set -o localoptions -o extendedglob
  local s=${(M)1%(#i)(${(j:|:k)~bsuffix})}
  (( ${1//(#bi)([0-9.][[:blank:]]#)(${(j:|:k)~bsuffix})/$match[1] * $bsuffix[$match[2]:l] } ))
}
Run Code Online (Sandbox Code Playgroud)

进而:

$ echo $(( b(1m + 1Mb) ))
2048576
Run Code Online (Sandbox Code Playgroud)

虽然存在e/ E( exa )的问题,但它在工作中放置了一个扳手,因为它1e-3GB不会被解释0.001 * 10000000001 * 1152921504606846976 - 3 * 1000000000.

在任何支持浮点运算(ksh93、zsh、yash)的 shell 中,你总是可以定义:

  K=1024  M=$((K * K))  G=$((M * K))  T=$((G * K))  P=$((T * K))  E=$((P * K))
KiB=$K  MiB=$M        GiB=$G        TiB=$T        PiB=$P        EiB=$E
 KB=1000 MB=$((KB*KB)) GB=$((MB*KB)) TB=$((GB*KB)) PB=$((TB*KB)) EB=$((PB*KB))
Run Code Online (Sandbox Code Playgroud)

或者打高尔夫球:

K=1024 EiB=$((E=K*(P=PiB=K*(T=TiB=K*(G=GiB=K*(M=MiB=K*K))))))
KB=1000 EB=$((EB=KB*(PB=KB*(TB=KB*(GB=KB*(MB=KB*KB))))))
Run Code Online (Sandbox Code Playgroud)

和写 $(( 1.1*GB + 5*K ))

要在输出上添加后缀,您可以使用 GNU numfmt

$ human() numfmt --field=- --to=iec --suffix=iB
$ echo $(( b(1m + 1Mb) )) | human
2.0MiB
Run Code Online (Sandbox Code Playgroud)


Qua*_*odo 5

Bcal

$ bcal -m "(5kib+2mib)/2"
1051136 B
$ bcal -m "(5kb+2mb)/2"
1002500 B
Run Code Online (Sandbox Code Playgroud)

-m标志用于简要输出。删除它会详细输出基数 2(KiB、MiB、GiB、TiB)和基数 10(kB、MB、GB、TB)的结果。

它不理解86kor320m1.7g,毕竟那些不是正确的字节单位。在这种情况下,您可以使用 Sedb在每个字母之后添加,然后将其通过管道传输到bcal

$ cat file
1.7g+320m+86k
$ sed 's/[gmk]/&b/g' file | bcal -m
bcal> 1.7gb+320mb+86kb
2020086000 B
Run Code Online (Sandbox Code Playgroud)

您也可以在交互模式下使用它。