将字节数转换为人类KiB MiB等的标准工具;像du,ls1


94

是否有一个标准工具可以将字节的整数计数转换为人类可读的最大可能的单位大小的计数,同时将数值保持在1.00和1023.99之间?

我有自己的bash / awk脚本,但是我正在寻找一个标准工具,该工具可以在许多/大多数发行版中找到……更通用的工具,理想情况下具有简单的命令行参数,并且/或者可以接受管道输入。

这是我要查找的输出类型的一些示例。

    1    Byt  
  173.00 KiB  
   46.57 MiB  
    1.84 GiB  
   29.23 GiB  
  265.72 GiB  
    1.63 TiB  

这是bytes-human脚本(用于以上输出)

awk -v pfix="$1" -v sfix="$2" 'BEGIN { 
      split( "Byt KiB MiB GiB TiB PiB", unit )
      uix = uct = length( unit )
      for( i=1; i<=uct; i++ ) val[i] = (2**(10*(i-1)))-1
   }{ if( int($1) == 0 ) uix = 1; else while( $1 < val[uix]+1 ) uix--
      num = $1 / (val[uix]+1)
      if( uix==1 ) n = "%5d   "; else n = "%8.2f"
      printf( "%s"n" %s%s\n", pfix, num, unit[uix], sfix ) 
   }'

更新  这里是吉尔斯脚本的修改版本,如对他的答案的评论..所述(修改为适合我的首选外观)。

awk 'function human(x) {
         s=" B   KiB MiB GiB TiB EiB PiB YiB ZiB"
         while (x>=1024 && length(s)>1) 
               {x/=1024; s=substr(s,5)}
         s=substr(s,1,4)
         xf=(s==" B  ")?"%5d   ":"%8.2f"
         return sprintf( xf"%s\n", x, s)
      }
      {gsub(/^[0-9]+/, human($1)); print}'

4
看来这里是我们正在制作的新内容standard tool:)
Gowtham,2012年

@Gowtham-您的愿望可能实现了!请参阅下面的答案或blog.frankleonhardt.com/2015/…–
FJL

请注意,最后两个后缀被交换;Yottabyte实际上大于Zettabyte。
staticfloat

Answers:


89

不,没有这样的标准工具。

由于GNU coreutils 8.21(2013年2月,因此尚未在所有发行版中提供)在非嵌入式Linux和Cygwin上可以使用numfmt。它不会产生完全相同的输出格式(从coreutils 8.23开始,我认为小数点后不能得到2位数字)。

$ numfmt --to=iec-i --suffix=B --padding=7 1 177152 48832200 1975684956
     1B
 173KiB
  47MiB
 1.9GiB

自coreutils 7.5起(2009年8月,因此出现在现代非嵌入式Linux发行版中),许多较旧的GNU工具都可以产生这种格式,GNU排序可以对单位进行数字排序


我发现您的代码有些混乱。这是更干净的awk版本(输出格式并不完全相同):

awk '
    function human(x) {
        if (x<1000) {return x} else {x/=1024}
        s="kMGTEPZY";
        while (x>=1000 && length(s)>1)
            {x/=1024; s=substr(s,2)}
        return int(x+0.5) substr(s,1,1)
    }
    {sub(/^[0-9]+/, human($1)); print}'

从一个更专业的问题转贴


好的谢谢。关于您的脚本,我基本上很喜欢。有几件事引起了我的注意:(1)var s应该有领导B。同样,该字符串很容易更改为IEC Binary表示法。(2)跳过了1000-1023的范围,转而使用1 <下一个尺寸>(易于更改)。(3)它没有十进制值(我确实想要)。同样,这很容易改变。当显示2个小数位时,该%f格式将a round-up表示为<next size>的值1019-1023;但这不值得解决。.我在回答中张贴了一个修改的版本,以供一般参考。
Peter.O 2012年

使用coreutils的osx自制用户使用
gnumfmt

对于那些想要将du数字转换为人类可读格式的用户,请注意,您可能需要添加--block-size=1du命令。
pawamoy

68

从v。开始8.21coreutils包括numfmt

numfmt读取各种表示形式的数字,并按要求重新格式化它们。
最常见的用法是将数字转换为 人类表示的形式。

例如

printf %s\\n 5607598768908 | numfmt --to=iec-i
5.2钛

各种其他例子(包括滤波,输入/输出处理等)被呈现该处


另外,从coreutilsv。开始8.24numfmt可以处理具有与相似的字段范围规范的多个字段cut,并支持使用--format选项设置输出精度,
例如

numfmt --to=iec-i --field=2,4 --format='%.3f' <<<'tx: 180000 rx: 2000000'
发射:175.782Ki接收:1.908Mi

numfmt是从coreutils-8.21开始向coreutils软件包中新添加的工具。
Zama Ques 2014年

1
现在,这应该是公认的答案。
安迪·福斯特

23

这是一个仅bash的选项,没有bc或没有任何其他非内置命令,+十进制格式和二进制单位。

bytesToHuman() {
    b=${1:-0}; d=''; s=0; S=(Bytes {K,M,G,T,P,E,Z,Y}iB)
    while ((b > 1024)); do
        d="$(printf ".%02d" $((b % 1024 * 100 / 1024)))"
        b=$((b / 1024))
        let s++
    done
    echo "$b$d ${S[$s]}"
}

例子:

$ bytesToHuman 123456789
117.73 MiB

$ bytesToHuman 1000000000000 # "1TB of storage"
931.32 GiB                   #  1TB of storage

$ bytesToHuman 
0 Bytes

应该可以在任何版本的Bash(包括Windows的MSYSGit的Bash)上良好运行。


这是我的bash需求的最佳答案。不幸的是,它在OP日期后的10年才发布1/2,这意味着要花一些时间才能提高投票名单。
WinEunuuchs2Unix

@ WinEunuuchs2Unix,谢谢,我很高兴它对您有所帮助:)
卡米洛·马丁

请注意,最后两个后缀被交换;Yottabyte实际上大于Zettabyte。
staticfloat

6

这是对Peter.O的Gilles awk脚本的修改版的启发而进行的完全重写。

变化:

  • 修复了Peter.O的错误,即他寻找一个> 1个字符的字符串,而他应该寻找一个> 4个字符的字符串。由于该错误,他的代码不适用于ZiB单元。
  • 消除了用空格分隔的一长串字符串的难看的硬编码。
  • 添加命令行开关以启用/禁用填充。
  • 添加命令行开关,以从base-1024(KiB)变为base-1000(KB)表示法。
  • 将所有内容包装在易于使用的功能中。
  • 我将此放在公共领域,欢迎广泛使用。

码:

bytestohuman() {
    # converts a byte count to a human readable format in IEC binary notation (base-1024), rounded to two decimal places for anything larger than a byte. switchable to padded format and base-1000 if desired.
    local L_BYTES="${1:-0}"
    local L_PAD="${2:-no}"
    local L_BASE="${3:-1024}"
    BYTESTOHUMAN_RESULT=$(awk -v bytes="${L_BYTES}" -v pad="${L_PAD}" -v base="${L_BASE}" 'function human(x, pad, base) {
         if(base!=1024)base=1000
         basesuf=(base==1024)?"iB":"B"

         s="BKMGTEPYZ"
         while (x>=base && length(s)>1)
               {x/=base; s=substr(s,2)}
         s=substr(s,1,1)

         xf=(pad=="yes") ? ((s=="B")?"%5d   ":"%8.2f") : ((s=="B")?"%d":"%.2f")
         s=(s!="B") ? (s basesuf) : ((pad=="no") ? s : ((basesuf=="iB")?(s "  "):(s " ")))

         return sprintf( (xf " %s\n"), x, s)
      }
      BEGIN{print human(bytes, pad, base)}')
    return $?
}

测试用例(如果要查看输出):

bytestohuman 1; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 500; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1023; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1024; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1500; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000000000; echo "${BYTESTOHUMAN_RESULT}.";

bytestohuman 1 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 500 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1023 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1024 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1500 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000000 no 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000000000 no 1000; echo "${BYTESTOHUMAN_RESULT}.";

bytestohuman 1 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 500 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1023 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1024 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1500 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000000 yes; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000000000 yes; echo "${BYTESTOHUMAN_RESULT}.";

bytestohuman 1 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 500 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1023 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1024 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 1500 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000000 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";
bytestohuman 150000000000000000000 yes 1000; echo "${BYTESTOHUMAN_RESULT}.";

请享用!


5

perlCPAN 上有几个模块:Format :: Human :: BytesNumber :: Bytes :: Human,后者更加完整:

$ echo 100 1000 100000 100000000 |
  perl -M'Number::Bytes::Human format_bytes' -pe 's/\d{3,}/format_bytes($&)/ge'
100 1000 98K 96M

$ echo 100 1000 100000 100000000 |
  perl -M'Number::Bytes::Human format_bytes' -pe 's/\d{3,}/
   format_bytes($&,bs=>1000, round_style => 'round', precision => 2)/ge'
100 1.00k 100k 100M

相反:

$ echo 100 1.00k 100K 100M 1Z |
  perl -M'Number::Bytes::Human parse_bytes' -pe '
    s/[\d.]+[kKMGTPEZY]/parse_bytes($&)/ge'
100 1024 102400 104857600 1.18059162071741e+21

注意:该功能parse_bytes()在版本0.09(2013-03-01)中添加


5

通过Linux-是否有用于字节计算的命令行计算器?-堆栈溢出,我发现了有关GNU单元的信息 -尽管SO页上没有示例;正如我未在此处列出的那样,这里有一个小注意事项。

首先,检查单位是否存在:

$ units --check-verbose |grep byte
doing 'byte'

$ units --check-verbose |grep mega
doing 'megalerg'
doing 'mega'

$ units --check-verbose |grep mebi
doing 'mebi'

既然是,请进行转换- printf接受格式说明符以格式化数字结果:

$ units --one-line -o "%.15g" '20023450 bytes' 'megabytes'  # also --terse
    * 20.02345
$ units --one-line -o "%.15g" '20023450 bytes' 'mebibytes' 
    * 19.0958499908447
$ units --one-line -o "%.5g" '20023450 bytes' 'mebibytes' 
    * 19.096

3

实际上,确实有一个实用程序可以做到这一点。我知道是因为我写的。它是为* BSD编写的,但是如果您拥有BSD库(我相信这很常见),则应该在Linux上编译。

我刚刚发布了一个新版本,发布在这里:

http://blog.frankleonhardt.com/2015/freebsd-hr-utility-human-可读-number-filter-man-page /

它称为hr,它将采用stdin(或文件)并将数字转换为人类可读的格式,这种方式(现在)与ls -h完全相同,依此类推,并且它可以按行选择单个提要,按比例缩放预缩放的单位(例如,如果它们以512字节块的形式将其转换为Mb等),调整列填充等。

几年前我写了它,是因为我认为尝试写一个shell脚本虽然从理论上讲很有趣,但是也很疯狂。

例如,使用hr,您可以轻松获得以下目录大小的排序列表(以1Kb为单位,转换前需要进行移位),其内容如下:

du -d1 | 排序-n | hr -sK

尽管du会生成-h输出,但sort不会对其进行排序。在现有实用程序中添加-h是不遵循unix原理的经典情况:让简单的实用程序很好地完成定义的作业。


2

这是一种几乎完全以bash方式进行的方法,只需要'bc'进行浮点运算即可。

function bytesToHR() {
        local SIZE=$1
        local UNITS="B KiB MiB GiB TiB PiB"
        for F in $UNITS; do
                local UNIT=$F
                test ${SIZE%.*} -lt 1024 && break;
                SIZE=$(echo "$SIZE / 1024" | bc -l)
        done

    if [ "$UNIT" == "B" ]; then
        printf "%4.0f    %s\n" $SIZE $UNIT
    else
        printf "%7.02f %s\n" $SIZE $UNIT
    fi
}

用法:

bytesToHR 1
bytesToHR 1023
bytesToHR 1024
bytesToHR 12345
bytesToHR 123456
bytesToHR 1234567
bytesToHR 12345678

输出:

   1    B
1023    B
   1.00 KiB
  12.06 KiB
 120.56 KiB
   1.18 MiB
  11.77 MiB

1
user@host:/usr$ alias duh="du -s -B1 * | sort -g | numfmt --to=iec-i --format='%10f'"
user@host:/usr$ duh

给出:

 4.0Ki games
 3.9Mi local
  18Mi include
  20Mi sbin
 145Mi bin
 215Mi share
 325Mi src
 538Mi lib

不幸的是,我不知道如何获得两位小数的精度。在Ubuntu 14.04上测试。


1

@don_crissti的第一个答案是好的,但是使用Here Strings可以更短,例如

$ numfmt --to=iec-i <<< "12345"
13Ki

$ numfmt --to=iec-i --suffix=B <<< "1234567"
1.2MiB

甚至

$ numfmt --from=iec-i --to=iec-i --suffix=B <<< "12345Ki"
13MiB

如果<<<不可用,可以使用例如

$ echo "1234567" | numfmt --to=iec-i --suffix=B
1.2MiB

1

存在Python工具

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

$humanfriendly --format-size=2048
2.05 KB
$humanfriendly --format-number=2048
2,048

我没有看到--binary标志:(,所以您必须直接使用python进行二进制表示:

$python -c 'import sys, humanfriendly; print(humanfriendly.format_size(int(sys.argv[1]), binary=True))' 2048
2 KiB
$python -c 'import sys, humanfriendly; print(humanfriendly.format_size(int(sys.argv[1]), binary=True))' 2000
1.95 KiB

1

我遇到了同样的问题,因此我很快使用awklog()功能提出了一个简单的解决方案:

awk '
  BEGIN {
    split("B,kiB,MiB,GiB", suff, ",")
  }

  {
    size=$1;
    rank=int(log(size)/log(1024));
    printf "%.4g%s\n", size/(1024**rank), suff[rank+1]
  }
'

而且使用浮点数所损失的精度也不错,因为无论如何都会损失该精度。


0

您的问题的答案是肯定的。

尽管输出格式不完全符合您的要求,但是转换本身可以通过非常标准的工具(或两个)轻松完成。我所指的是dcbc。您可以通过更改其输出半径获得分段报告。像这样:

{   echo 1024 o           #set dc's output radix
    echo 1023 pc          #echo a number then print + clear commands
    echo 1024 pc
    echo 1025 pc
    echo 8000000 pc
} | dc

...打印...

 1023                    #1 field 1023 bytes
 0001 0000               #2 fields 1k 0b
 0001 0001               #2 fields 1k 1b
 0007 0644 0512          #3 fields 7m 644k 512b or 7.64m

dc在上面使用它是因为它是个人的最爱,但是bc可以使用不同的语法执行相同的操作,并遵循POSIX指定的相同格式规则,例如:

  • bc 观察

    • 对于大于16的基数,每个数字应写为一个单独的多数字十进制数字。除最高有效小数位数外,每个数字前均应加一个空格。对于从17到100的基数,bc应写两位十进制数;对于从101到1000的基数,三位数的十进制字符串,依此类推。例如,以25为基的十进制数字1024将写为:

    01 15 24

    并以125为基:

    008 024


-1

简短而甜美,仅外壳解决方案:

convertB_human() {
NUMBER=$1
for DESIG in Bytes KB MB GB TB PB
do
   [ $NUMBER -lt 1024 ] && break
   let NUMBER=$NUMBER/1024
done
printf "%d %s\n" $NUMBER $DESIG
}

它不显示小数部分。

let VAR=expression是光荣的。用VAR=$(( expression ))再次出生替换。


由于/ 1024总是四舍五入,因此该解决方案会引入大量错误,我敢肯定您不想将1.5 TiB舍入为2 TiB。
Geoffrey

-2

AFAIK没有这样的标准工具,您可以向其传递文本,并且它返回人类可读的形式。您可能可以找到一个软件包来完成发行版的上述任务。

但是,我不明白为什么您可能需要这样的工具。提供相关输出的大多数软件包通常都具有-h或等效开关以供人类阅读。


1
出于理解的目的:人类可读的含义仅在于此;人类可读。您提到的工具显示的各种大小的不同单位均不适用于程序计算,对于这些目的,单位的一致性至关重要。bash始终是整数,这是bash对其进行任何算术运算的唯一方法。因此...以字节为单位计算...以人为单位报告,例如。“您将永久删除3个文件,总计2.44 GiB。继续吗?
Peter.O 2012年

我认为这应该是您的问题的一部分。在我看来,您已经解决了问题。祝好运。
Shellter

1
常见的应用是生成用于排序的字节数,并在排序后转换为人类可读的单位。
吉尔斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.