是否有一个标准工具可以将字节的整数计数转换为最大可能的单位大小的人类可读计数,同时将数值保持在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  


这里是字节人工脚本(用于以上输出)

awk -v pfix="" -v sfix="" '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() == 0 ) uix = 1; else while(  < val[uix]+1 ) uix--
      num =  / (val[uix]+1)
      if( uix==1 ) n = "%5d   "; else n = "%8.2f"
      printf( "%s"n" %s%s\n", pfix, num, unit[uix], sfix ) 
   }'


更新此处是Gilles脚本的修改版本,如对他的答案的评论中所述(修改为适合我的偏爱外观)。

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()); print}'


评论

看起来这里我们正在制作一个新的标准工具:)

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

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

#1 楼

不,没有这样的标准工具。
由于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开始,较早的GNU工具就可以产生这种格式,而GNU排序可以对单位进行数字排序(2009年8月,因此出现在现代非嵌入式Linux发行版中)。


我发现您的代码有点令人费解。这是一个更干净的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()); print}'


(从一个更专业的问题转发)

评论


好的谢谢。关于您的脚本,我基本上很喜欢。有几件事引起了我的注意:(1)var s应该以B开头。而且此字符串很容易更改为IEC Binary表示法。 (2)跳过1000-1023范围,转而使用1 <下一个尺寸>(易于更改)。(3)它没有十进制值(我确实想要)。同样,这很容易改变。当显示2个小数位时,%f格式会导致对1019-1023的值进行四舍五入为;但这不值得解决。.我在回答中发布了修改后的版本,以供一般参考。

– Peter.O
2012年7月27日在8:17

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

–verboze
16-3-9在22:11

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

–pawamoy
18-10-25在19:51



#2 楼

8.21版本开始,coreutils包括numfmt


numfmt读取各种表示形式的数字并按要求将其重新格式化

最常见的用法是将数字转换为/来自
人的代表。


例如

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


5.2Ti


其他各种



此外,从coreutils v。8.24开始,numfmt可以处理字段范围规格类似于cut的多个字段,并支持使用--format选项设置输出精度。
例如

numfmt --to=iec-i --field=2,4 --format='%.3f' <<<'tx: 180000 rx: 2000000'


tx: 175.782Ki rx: 1.908Mi


评论


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

– Zama Ques
2014年3月24日10:50

现在,这应该是公认的答案。

–安迪·福斯特(Andy Foster)
18-10-5在19:20

#3 楼

这是一个仅限bash的选项,没有bc或任何其他非内置命令,+十进制格式和二进制单位。
# Converts bytes value to human-readable string [: bytes value]
bytesToHumanReadable() {
    local i=${1:-0} d="" s=0 S=("Bytes" "KiB" "MiB" "GiB" "TiB" "PiB" "EiB" "YiB" "ZiB")
    while ((i > 1024 && s < ${#S[@]}-1)); do
        printf -v d ".%02d" $((i % 1024 * 100 / 1024))
        i=$((i / 1024))
        s=$((s + 1))
    done
    echo "$i$d ${S[$s]}"
}

示例:
$ bytesToHumanReadable 123456789
117.73 MiB

$ bytesToHumanReadable 1000000000000 # '1TB of storage'
931.32 GiB                           #  1TB of storage

$ bytesToHumanReadable 
0 Bytes

$ bytesToHumanReadable 9223372036854775807
7.99 EiB

应在任何版本的在那里重击(包括Windows的MSYSGit的Bash)。

评论


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

–WinEunuuchs2Unix
17年4月22日在15:46

@ WinEunuuchs2Unix谢谢,很高兴它对您有所帮助:)

–卡米洛·马丁(Camilo Martin)
17年5月17日在23:01

@Maxxim:我们可以确切地看到您所做的更改。编辑注释应该更多地是摘要(例如,“重构代码;在while循环中增加上限”。),也许还有一些道理(例如,“通过使用'printf -v'避免子shell”)。尝试将我的编辑评论最多保留150个字符(您的is275)。 …………………………………………………………………………P.S.为什么要麻烦“对所有引用都使用双引号”?为何您说所有报价都使用双引号,然后将“ 1TBofstorage”更改为“ 1TBofstorage”?

– G-Man说“恢复莫妮卡”
20 Jun 18'21:46



P.P.S.一些外壳会进行一些优化。如果命令是内置的,则$(command)不一定会生成子外壳。

– G-Man说“恢复莫妮卡”
20年6月18日在21:55

@ G-ManSays'ReinstateMonica':这个特定的答案是关于Bash的,当使用$()时Bash会生成子外壳,从而大大降低了性能。如果只调用一次函数就不成问题了,但是如果您在循环中使用它,就很明显了。默认情况下,Bash代码中的所有引号均使用双引号(除非您不希望变量扩展),因此这只是一种良好的编码风格,仅此而已。 “ 1TBofstorage”是注释中的文字引号,在英语中,您不会使用双引号。

– Maxxim
20年6月19日在18:57



#4 楼

通过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


#5 楼

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

更改:


修复了Peter.O的错误,他在其中查找了> 1个字符的字符串,他应该在其中查找一个> 4个字符。由于该错误,他的代码不适用于ZiB单元。
删除了用空格分隔的一长串字符串的非常难看的硬编码。
添加了命令行开关以启用/禁用填充。 br />添加了命令行开关,以从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}.";


享受吧!

#6 楼

CPAN上有几个perl模块:Format :: Human :: Bytes和Number :: 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


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

#7 楼

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

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

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

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

我几年前写了它,因为我想尝试编写一个shell脚本,尽管从理论上讲很有趣

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

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

du将产生-h输出,而sort不会按它排序。在现有实用程序中添加-h是不遵循unix原理的经典情况:让简单的实用程序很好地完成定义的作业。

评论


下载链接似乎已损坏...

– David Z
20年5月2日,7:27

@DavidZ:我从archive.org抓取了tarball,并上传了一个可以克隆的小git仓库:git clone https://bitbucket.org/mburr/hr.git

– Michael Burr
20年7月9日在7:38

#8 楼

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

function bytesToHR() {
        local SIZE=
        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


#9 楼

我遇到了同样的问题,我很快就使用awklog()函数想出了一个简单的解决方案:

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

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


使用浮点数所损失的精度还不错。因为无论如何都会失去精度。

#10 楼

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上进行了测试。

#11 楼

@don_crissti的第一个答案是好的,但使用此处字符串甚至可以更短,例如

$ 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


#12 楼

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

虽然输出格式不完全符合您的规范,但是转换本身可以通过非常标准的工具(或两个)轻松完成。我所指的是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 obase


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

01 15 24

,以125为基数,写为:

008 024



#13 楼

存在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


#14 楼

如果可以使用Python和pip,则可以通过人性化解决。 (感谢Pyrocater的建议。)

$ pip install humanize
$ bytes=35672345337
$ python -c "import humanize; print(humanize.naturalsize($bytes))"

35.7 GB

$ seq 0 750000 2250000 |python -c $'import sys, humanize\nfor n in sys.stdin: print(humanize.naturalsize(n))'

0 Bytes
750.0 kB
1.5 MB
2.2 MB


#15 楼

pip install humanfriendly
,然后向您的默认外壳添加一个简单函数(例如~/.bashrc

function fsize() { humanfriendly --format-size `stat -f '%z' ` }


像这样使用

➜ fsize file.txt
6.17 KB


#16 楼

我终于为此做了一个工具。
https://github.com/gonejack/hsize
go get -u github.com/gonejack/hsize

> echo 31415 | hsize
31415 => 30.68KB

> hsize 46783 92929211
46783 => 45.69KB
92929211 => 88.62MB


评论


欢迎来到该网站。如果您与所建议的软件关联,这不成问题-恳请添加关于此的公开声明。

– AdminBee
20/11/15在9:14

#17 楼

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

convertB_human() {
NUMBER=
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 ))代替再次出生。

评论


@Geoffrey:整数除法是在什么环境下进行的?

– G-Man说“恢复莫妮卡”
20年6月18日在21:46

@ G-ManSays'ReinstateMonica'是正确的,此评论写于多年前,在我不完全了解整数除法的工作原理之前,但是,如果您关心OP的分数,它仍然会在每次迭代中失去精度。

–杰弗里
20年6月19日在1:24

@Geoffrey:我不知道这是怎么回事。所有答案都是“精度降低”,因为它们只显示两位小数,这似乎是问题的所在。如果将要求更改为显示三个以上的十进制数字,那么您可能有一个要点-但在这种情况下,您的注释也适用于所有其他答案。您能否举一个例子说明此答案如何产生与问题所要求的结果不同的结果?

– G-Man说“恢复莫妮卡”
20年6月19日,下午3:22

除了这个答案,这里的每个答案都考虑到了OP想要的结果精确到两位小数的事实。这个答案是使用纯整数除法,因此只能使用整数除法,除非有人使用固定精度数学来使用纯整数除法获得期望的结果,否则没有办法。

–杰弗里
20年6月20日在5:16

#18 楼

AFAIK没有这样的标准工具,您可以向其传递文本,并且它返回人类可读的形式。
您也许可以找到一个软件包来完成发行版中的上述任务。大多数提供相关输出的软件包通常都具有-h或等效开关,以供人类阅读。

评论


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

– Peter.O
2012年7月26日在16:13



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

–脱壳机
2012年7月26日在22:20

常见的应用是生成用于排序的字节数,并在排序后转换为人类可读的单位。

–吉尔斯'所以-不再是邪恶的'
2012年7月26日23:44