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

94

Existe uma ferramenta padrão que converte uma contagem inteira de bytes em uma contagem legível por humanos do maior tamanho de unidade possível, mantendo 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 , encontrada em muitas / muitas distros ... algo mais geralmente disponível e, idealmente, possui argumentos de linha de comando simples 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 de Gilles , conforme descrito em um comentário à resposta dele (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}'
Peter.O
fonte
4
Parece que aqui temos uma nova standard toolna tomada :)
Gowtham
@Gowtham - seu desejo pode ter se tornado realidade! Veja minha resposta abaixo ou blog.frankleonhardt.com/2015/…
FJL
Observe que os dois últimos sufixos são trocados; um Yottabyte é realmente maior que um Zettabyte.
### staticfloat #

Respostas:

89

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

Desde o GNU coreutils 8.21 (fevereiro de 2013, ainda não presente em todas as distribuições), no Linux e Cygwin não incorporado, você pode usar numfmt. Ele não produz exatamente o mesmo formato de saída (a partir do coreutils 8.23, acho que você não 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 esse formato e a classificação GNU pode classificar números com unidades desde o coreutils 7.5 (agosto de 2009, tão presente nas modernas distribuições Linux não incorporadas).


Acho seu código um pouco complicado. Aqui está uma versão mais limpa do awk (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}'

( Enviado a partir de uma pergunta mais especializada )

Gilles
fonte
Ok obrigado. Sobre o seu roteiro, eu basicamente gosto muito. Há algumas coisas que me chamaram a atenção: (1) var sdeveria estar liderando B. Além disso, essa string é facilmente alterada para notação binária IEC. (2) Ignora o intervalo 1000-1023 em favor de 1 <próximo tamanho> (facilmente alterado) (3) Não possui valores decimais (o que eu quero). Novamente, isso é facilmente alterado. Ao exibir duas casas decimais, o %fformato causa a round-uppara o <próximo tamanho> para os valores 1019-1023 ; mas não vale a pena uma solução alternativa. Publiquei uma versão modificada na minha resposta, para referência geral.
Peter.O
gnumfmt para usuários homebrew OSX usando coreutils
verboze
Para aqueles que desejam converter dunúmeros em formato legível por humanos, observe que pode ser necessário adicionar --block-size=1ao ducomando.
pawamoy
68

A partir da versão v. 8.21, coreutilsInclui numfmt:

numfmtlê números em várias representações e reformata-os conforme solicitado.
O uso mais comum é converter números de / para representação humana .

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 coreutilsv. 8.24, numfmtPode processar vários campos com especificações faixa de campo semelhantes cut, e suporta definir a precisão de saída com a --formatopção
por exemplo,

numfmt --to=iec-i --field=2,4 --format='%.3f' <<<'tx: 180000 rx: 2000000'
tx: 175.782Ki rx: 1.908Mi
don_crissti
fonte
O numfmt é uma ferramenta recém-adicionada ao pacote coreutils a partir do coreutils-8.21.
Zama Ques 24/03
1
Agora, essa deve ser a resposta aceita.
Andy Foster
23

Aqui está uma opção apenas para o bash, nenhum bcou qualquer outro não embutido, + 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 ter bom desempenho em qualquer versão do Bash existente (incluindo o BYS para Windows do MSYSGit).

Camilo Martin
fonte
Esta é a melhor resposta para minhas necessidades do bash. Infelizmente, é publicado 1/2 uma década após a data do OP, o que significa que levará um tempo para subir na lista de votações.
WinEunuuchs2Unix
@ WinEunuuchs2Unix obrigado, fico feliz que tenha sido útil para você :) #
Camilo Martin
Observe que os dois últimos sufixos são trocados; um Yottabyte é realmente maior que um Zettabyte.
### staticfloat #
6

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

Alterar:

  • Corrige o bug de Peter.O, onde ele procura uma sequência de> 1 caractere, onde ele deve procurar 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 unidades separadas por espaço.
  • Adiciona opções de linha de comando para ativar / desativar o preenchimento.
  • Adiciona opções de linha de comando para passar da notação base-1024 (KiB) para base-1000 (KB).
  • Envolve tudo em uma função fácil de usar.
  • 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}.";

Desfrutar!

John
fonte
5

Existem alguns perlmódulos no CPAN: Format :: Human :: Bytes e Number :: Bytes :: Human , sendo o ú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 (01/03/2013)

Stéphane Chazelas
fonte
5

Via linux - Existe uma calculadora de linha de comando para cálculos de bytes? - Stack Overflow , eu descobri sobre as unidades GNU - 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 - printfespecificadores de formato são aceitos 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
sdaau
fonte
3

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

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

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

Chama-se hr e leva stdin (ou arquivos) e converte números para o formato legível por humanos de uma maneira que (agora) é exatamente igual a ls -h e assim por diante, e pode selecionar feeds individuais em linhas, escala unidades pré-dimensionadas (por exemplo, se estiverem em blocos de 512 bytes, convertê-las em Mb, etc.), ajuste o preenchimento das colunas e assim por diante.

Escrevi alguns anos atrás, porque achava que tentar escrever um shell script, embora intelectualmente interessante, também fosse uma loucura total.

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

du -d1 | classificar -n | hr -sK

Enquanto du produzirá a saída -h, a classificação não será classificada por ela. A adição de -h aos utilitários existentes é um caso clássico de não seguir a filosofia do unix: ter utilitários simples realizando tarefas definidas muito bem.

FJL
fonte
2

Aqui está uma maneira de fazê-lo quase puramente no bash, só precisa de 'bc' para a matemática de 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

Resultado:

   1    B
1023    B
   1.00 KiB
  12.06 KiB
 120.56 KiB
   1.18 MiB
  11.77 MiB
Geoffrey
fonte
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 duas casas decimais. Testado no Ubuntu 14.04.

Chris
fonte
1

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

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

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

ou 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
craeckie
fonte
1

Existem ferramentas 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 uma flag --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
ThorSummoner
fonte
1

Eu tive o mesmo problema e rapidamente criei uma solução simples usando awka log()função:

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 flutuantes não é tão ruim, pois essa precisão será perdida de qualquer maneira.

Bence Kiglics
fonte
0

A resposta para sua pergunta é sim.

Embora o formato de saída não corresponda exatamente às suas especificações, a conversão em si é facilmente realizada por uma ferramenta muito padrão (ou duas) . Os que eu me refiro são dce bc. Você pode obter um relatório segmentado alterando as radias de saída. Como isso:

{   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 dcacima porque é um favorito pessoal, mas bcpode fazer o mesmo com sintaxe diferente e adere às mesmas regras de formato especificadas pelo POSIX, como:

  • bc obase

    • Para bases maiores que 16, cada dígito deve ser escrito como um número decimal com vários dígitos. 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, bcdeve 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

mikeserv
fonte
-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é Korn-ish. Substitua VAR=$(( expression ))por Born-again-ish.

Johan
fonte
Esta solução apresenta uma tonelada de erro, pois o / 1024 sempre arredonda, tenho certeza que você não deseja arredondar 1,5 TiB para 2 TiB.
9133 Geoffrey
-2

AFAIK não existe uma ferramenta padrão para a qual você pode passar texto e ele retorna um formulário legível por humanos. Você pode encontrar um pacote para realizar a tarefa mencionada para sua distribuição.

No entanto, não entendo por que você pode precisar dessa ferramenta. A maioria dos pacotes que fornecem uma saída relacionada geralmente possui uma opção -h ou equivalente para saída legível por humanos.

darnir
fonte
1
Para fins de entendimento: legível por humanos significa exatamente isso; legível por seres humanos. As várias unidades diferentes de tamanho mostradas pelas ferramentas mencionadas não se destinam a cálculos programáticos, para os quais a uniformidade das unidades é essencial. Trabalhar com bytes, que sempre são números inteiros, é a única maneira de o bash fazer aritmética com eles. Então ... calcular em bytes ... reportar em humano , por exemplo. "Você está prestes a excluir permanentemente 3 arquivos, totalizando 2,44 GiB. Continuar?"
Peter.O
Eu acho que isso deve fazer parte da sua pergunta. Parece-me que você resolveu o problema. Boa sorte.
shellter 26/07/12
1
Um aplicativo comum é gerar números de bytes para classificação e converter em unidades legíveis por humanos após a classificação.
Gilles