Uma ferramenta padrão para converter uma contagem de bytes em humano KiB MiB etc; como du, ls1

82

Existe uma ferramenta padrão que converte uma contagem de inteiros de Bytes em uma contagem legível por humanos do maior tamanho de unidade possível, enquanto mantém o valor numérico entre 1,00 e 1023,99?

Eu tenho meu próprio script bash / awk, mas estou procurando uma ferramenta padrão , que é encontrada em muitas / mais distribuições ... algo mais disponível e, idealmente, possui uma linha de comando simples args e / ou pode aceitar entrada canalizada.

Aqui estão alguns exemplos do tipo de saída que estou procurando.

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

Aqui está o script bytes-human (usado para a saída acima)

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 ) 
   }'

Atualizar Aqui está uma versão modificada do script Gilles ', como descrito em um comentário à sua resposta .. (modificado para se adequar à minha aparência preferida).

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}'
    
por Peter.O 26.07.2012 / 17:03

15 respostas

74

Não, não existe essa ferramenta padrão.

Desde o GNU coreutils 8.21 (fevereiro de 2013, portanto ainda não está presente em todas as distribuições), no Linux não integrado e no Cygwin, você pode usar numfmt . Ele não produz exatamente o mesmo formato de saída (como do coreutils 8.23, eu não acho que você pode obter 2 dígitos após os pontos decimais).

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

Muitas ferramentas GNU mais antigas podem produzir este formato e O tipo GNU pode ordenar números com unidades desde o coreutils 7.5 (agosto de 2009, assim presente em distribuições Linux não-embarcadas modernas).

Eu acho seu código um pouco confuso. Aqui está uma versão awk mais limpa (o formato de saída não é exatamente idêntico):

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}'

( republicado de uma pergunta mais especializada )

    
por 27.07.2012 / 02:19
59

A partir de v. 8.21 , coreutils inclui numfmt :

numfmt reads numbers in various representations and reformats them as requested.
The most common usage is converting numbers to / from human representation.

por exemplo,

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

Vários outros exemplos (incluindo filtragem, processamento de entrada / saída, etc.) são apresentados AQUI .

Além disso, a partir de coreutils v. 8.24 , numfmt pode processar vários campos com especificações de intervalo de campo semelhantes a cut e suporta a configuração da precisão de saída com a opção --format
por exemplo,

numfmt --to=iec-i --field=2,4 --format='%.3f' <<<'tx: 180000 rx: 2000000'
tx: 175.782Ki rx: 1.908Mi
    
por 30.04.2013 / 21:11
19

Aqui está uma opção somente bash, sem bc ou qualquer outra não-builtins, + formato decimal e unidades binárias.

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]}"
}

Exemplos:

$ bytesToHuman 123456789
117.73 MiB

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

$ bytesToHuman 
0 Bytes

Deve funcionar bem em qualquer versão do Bash (incluindo o Bash para Windows do MSYSGit).

    
por 02.02.2016 / 08:55
5

Esta é uma reescrita completa inspirada na versão modificada de Peter.O do script awk de Gilles.

Mudanças:

  • Corrige o erro de Peter.O, onde ele procura por uma sequência de caracteres > 1 em que ele deve procurar por um > 4 caracteres. Devido a esse bug, seu código não funciona para unidades ZiB.
  • Remove a codificação muito feia de uma longa sequência de tamanhos de unidade separados por espaço.
  • Adiciona opções de linha de comando para ativar / desativar o preenchimento.
  • Adiciona opções de linha de comando para ir da notação base-1024 (KiB) à base-1000 (KB).
  • Envolve tudo em uma função fácil de usar.
  • Eu coloco isso em domínio público e saúdo o uso generalizado.

Código:

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 $?
}

Casos de teste (se você quiser ver a saída):

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}.";

Aproveite!

    
por 04.11.2013 / 10:16
5

Existem alguns módulos perl no CPAN: Formato :: Humano :: Bytes e Number :: Bytes :: Human , sendo este último um pouco mais completo:

$ 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

E o contrário:

$ 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

NOTA: a função parse_bytes() foi adicionada na versão 0.09 (2013-03 -01)

    
por 15.04.2013 / 22:28
4

Por linux - Existe uma calculadora de linha de comando para byte cálculos? - Stack Overflow , eu encontrei sobre GNU Units - embora sem exemplos na página SO; e como eu não o vi listado aqui, aqui está uma pequena nota sobre isso.

Primeiro, verifique se as unidades estão presentes:

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

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

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

Dado que são, faça uma conversão - são aceites% de especificadores de formatoprintf para formatar o resultado numérico:

$ 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
    
por 26.01.2015 / 09:00
3

Na verdade, existe um utilitário que faz exatamente isso. Eu sei porque fui eu quem escreveu isso. Ele foi escrito para * BSD, mas deve ser compilado no Linux se você tiver as bibliotecas BSD (que eu acredito serem comuns).

Acabei de lançar uma nova versão, postada aqui:

link

Ele é chamado de hr, e vai levar stdin (ou arquivos) e converter números para o formato legível de uma forma que é (agora) exatamente o mesmo que ls -h e assim por diante, e pode selecionar feeds individuais em linhas, dimensionar unidades pré-dimensionadas (por exemplo, se estiverem em blocos de 512 bytes, convertê-las em Mb, etc.), ajustar o preenchimento de colunas e assim por diante.

Eu escrevi isso há alguns anos porque eu pensei que tentar escrever um script de shell, embora intelectualmente interessante, também era loucura total.

Usando hr, por exemplo, você pode facilmente obter uma lista ordenada de tamanhos de diretório (que saem em unidades de 1Kb e precisam ser deslocados antes da conversão) com o seguinte:

du -d1 | sort -n | hr -sK

Enquanto du produzir a saída -h, a ordenação não será ordenada por ela. A adição de -h aos utilitários existentes é um caso clássico de não seguir a filosofia unix: ter utilitários simples que executem trabalhos definidos muito bem.

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

Dá:

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

Infelizmente, não consigo descobrir como obter precisão de dois decimais. Testado no Ubuntu 14.04.

    
por 13.04.2015 / 10:14
1

Aqui está uma maneira de fazer isso quase puramente no bash, só precisa de 'bc' para a matemática do ponto flutuante.

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
}

Uso:

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

Saída:

   1    B
1023    B
   1.00 KiB
  12.06 KiB
 120.56 KiB
   1.18 MiB
  11.77 MiB
    
por 06.08.2015 / 04:41
1

A primeira resposta de @ don_crissti é boa, mas pode ser ainda mais curta usando Aqui Strings , por exemplo

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

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

ou até mesmo

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

se <<< não estiver disponível, você pode usar, por exemplo,

$ echo "1234567" | numfmt --to=iec-i --suffix=B
1.2MiB
    
por 23.10.2015 / 13:28
1

Existem ferramentas em 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

Eu não vejo um sinalizador --binary :(, então você teria que usar python diretamente para representação binária:

$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
    
por 22.09.2017 / 23:36
1

Eu tive o mesmo problema e rapidamente encontrei uma solução simples usando awk ' log() function:

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]
  }
'

E a precisão perdida no uso de números float não é tão ruim, já que a precisão será perdida de qualquer maneira.

    
por 16.07.2018 / 11:43
0

A resposta para sua pergunta é sim.

Embora o formato de saída não seja exatamente da sua especificação, a conversão em si é facilmente feita por uma ferramenta muito normal (ou duas) . Aqueles aos quais me refiro são dc e bc . Você pode obter um relatório segmentado alterando suas radices de saída. Assim:

{   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

... que imprime ...

 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

Eu uso dc acima porque é um favorito pessoal, mas bc pode fazer o mesmo com uma sintaxe diferente e segue as mesmas regras de formato especificadas por POSIX como:

  • bc obase

    • Para bases maiores que 16, cada dígito deve ser escrito como um número decimal de vários dígitos separado. Cada dígito, exceto o dígito fracionário mais significativo, deve ser precedido por um único espaço . Para bases de 17 a 100, bc deve escrever números decimais de dois dígitos; para bases de 101 a 1000, cadeias decimais de três dígitos e assim por diante. Por exemplo, o número decimal 1024 na base 25 seria escrito como:

    01 15 24

    e na base 125, como:

    008 024

por 22.03.2015 / 16:10
-1

AFAIK não existe ferramenta padrão para a qual você pode passar texto e retorna um formulário legível. Você pode encontrar um pacote para realizar a tarefa para sua distro.

No entanto, não entendo por que você pode precisar de uma ferramenta desse tipo. A maioria dos pacotes que fornecem uma saída relacionada geralmente tem um switch -h ou equivalente para uma saída legível por humanos.

    
por 26.07.2012 / 17:06
-1

Solução curta e doce, apenas com casca:

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
}

Não mostra a poção decimal.

O let VAR=expression é o Korn-ish. Substitua com VAR=$(( expression )) por nascido de novo.

    
por 11.08.2014 / 11:36