Una herramienta estándar para convertir un conteo de bytes en KiB MiB humano, etc. como du, ls1

94

¿Existe una herramienta estándar que convierta un recuento entero de bytes en un recuento legible por humanos del tamaño de unidad más grande posible, mientras se mantiene el valor numérico entre 1.00 y 1023.99?

Tengo mi propio script bash / awk, pero estoy buscando una herramienta estándar , que se encuentra en muchas / la mayoría de las distribuciones ... algo más generalmente disponible, e idealmente tiene argumentos simples de línea de comandos, y / o puede aceptar entradas entubada.

Aquí hay algunos ejemplos del tipo de salida que estoy buscando.

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

Aquí está el script bytes-humano (utilizado para la salida anterior)

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

Actualización  Aquí hay una versión modificada del guión de Gilles , como se describe en un comentario a su respuesta ... (modificado para adaptarse a mi aspecto preferido).

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
fuente
44
Parece que aquí tenemos una nueva standard toolcreación :)
Gowtham
@Gowtham: ¡su deseo puede haberse hecho realidad! Véase mi respuesta por debajo o blog.frankleonhardt.com/2015/...
FJL
Tenga en cuenta que los dos últimos sufijos se intercambian; un Yottabyte es en realidad más grande que un Zettabyte.
staticfloat

Respuestas:

89

No, no existe una herramienta estándar de este tipo.

Desde GNU coreutils 8.21 (febrero de 2013, por lo que todavía no está presente en todas las distribuciones), en Linux y Cygwin no integrados, puede usar numfmt. No produce exactamente el mismo formato de salida (a partir de coreutils 8.23, no creo que pueda obtener 2 dígitos después de los puntos decimales).

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

Muchas herramientas antiguas de GNU pueden producir este formato y la ordenación de GNU puede ordenar números con unidades desde coreutils 7.5 (agosto de 2009, tan presente en las distribuciones modernas de Linux no incrustadas).


Encuentro su código un poco complicado. Aquí hay una versión awk más limpia (el formato de salida no es exactamente 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}'

( Publicado de una pregunta más especializada )

Gilles
fuente
Bien gracias. Sobre tu guión, básicamente me gusta mucho. Hay algunas cosas que me llamaron la atención: (1) var sdebería ser líder B. Además, esta cadena se cambia fácilmente a notación binaria IEC. (2) Se salta el rango 1000-1023 a favor de 1 <tamaño siguiente> (fácil de cambiar) (3) No tiene valores decimales (que sí quiero). De nuevo, esto se cambia fácilmente. Cuando se muestran 2 lugares decimales, el %fformato causa un round-up<tamaño siguiente> para los valores 1019-1023 ; pero no vale la pena una solución alternativa. He publicado una versión modificada en mi respuesta, para referencia general.
Peter.O
gnumfmt para usuarios de osx homebrew que usan coreutils
verboze el
Para aquellos que desean convertir dunúmeros a un formato legible para humanos, tenga en cuenta que es posible que deba agregar --block-size=1al ducomando.
pawamoy
68

A partir del v. 8.21, coreutilsIncluye numfmt:

numfmtlee los números en varias representaciones y los reformatea según lo solicitado.
El uso más común es convertir números a / desde representación humana .

p.ej

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

AQUÍ se presentan varios otros ejemplos (incluido el filtrado, el procesamiento de entrada / salida, etc.) .


Además, a partir de coreutilsv. 8.24, numfmtPuede procesar múltiples campos con especificaciones de rango de campo similares cuty admite la configuración de la precisión de salida con la --formatopción,
por ejemplo

numfmt --to=iec-i --field=2,4 --format='%.3f' <<<'tx: 180000 rx: 2000000'
tx: 175.782Ki rx: 1.908Mi
don_crissti
fuente
numfmt es una herramienta recientemente agregada al paquete coreutils desde coreutils-8.21 en adelante.
Zama Ques
1
Esta debería ser ahora la respuesta aceptada.
Andy Foster
23

Aquí hay una opción de solo bash, ninguna bco ninguna otra no integrada, + formato decimal y unidades binarias.

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

Ejemplos:

$ bytesToHuman 123456789
117.73 MiB

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

$ bytesToHuman 
0 Bytes

Debería funcionar bien en cualquier versión de Bash (incluida MSYSGit's Bash para Windows).

Camilo Martin
fuente
Esta es la mejor respuesta para mis necesidades de bash. Desafortunadamente, se publicó 1/2 década después de la fecha de OP, lo que significa que llevará un tiempo avanzar en la lista de votación.
WinEunuuchs2Unix
@ WinEunuuchs2Unix gracias, me alegro de que te haya sido útil :)
Camilo Martin
Tenga en cuenta que los dos últimos sufijos se intercambian; un Yottabyte es en realidad más grande que un Zettabyte.
staticfloat
6

Esta es una reescritura completa inspirada en la versión modificada de Peter.O del guión awk de Gilles.

Cambios:

  • Soluciona el error de Peter.O donde busca una cadena de> 1 carácter donde debería estar buscando uno> 4 caracteres. Debido a ese error, su código no funciona para las unidades ZiB.
  • Elimina la codificación muy fea de una larga cadena de tamaños de unidades separadas por espacios.
  • Agrega interruptores de línea de comandos para habilitar / deshabilitar el relleno.
  • Agrega cambios de línea de comando para pasar de la notación base-1024 (KiB) a la base-1000 (KB).
  • Lo envuelve todo en una función fácil de usar.
  • Coloco esto en el dominio público y agradezco el 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 prueba (si desea ver la salida):

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

¡Disfrutar!

John
fuente
5

Hay un par de perlmódulos en CPAN: Formato :: Humano :: Bytes y Número :: Bytes :: Humano , el último es un poco más 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

Y a la inversa:

$ 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: la función parse_bytes()se agregó en la versión 0.09 (2013-03-01)

Stéphane Chazelas
fuente
5

Via linux: ¿hay una calculadora de línea de comandos para los cálculos de bytes? - Desbordamiento de pila , encontré sobre unidades GNU , aunque sin ejemplos en la página SO; y como no lo vi listado aquí, aquí hay una pequeña nota al respecto.

Primero, verifique si las unidades están 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 lo son, realice una conversión: los printfespecificadores de formato se aceptan para formatear el 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
fuente
3

En realidad, hay una utilidad que hace exactamente esto. Lo sé porque fui yo quien lo escribió. Fue escrito para * BSD pero debería compilarse en Linux si tiene las bibliotecas BSD (que creo que son comunes).

Acabo de lanzar una nueva versión, publicada aquí:

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

Se llama hr, y tomará stdin (o archivos) y convertirá números a formato legible para humanos de una manera que (ahora) sea exactamente la misma que ls -h y así sucesivamente, y puede seleccionar feeds individuales en líneas, escala unidades preescaladas (por ejemplo, si están en bloques de 512 bytes, conviértalas a Mb, etc.), ajuste el relleno de la columna, etc.

Lo escribí hace unos años porque pensé que tratar de escribir un script de shell, aunque intelectualmente interesante, también era una locura total.

Usando hr, por ejemplo, puede obtener fácilmente una lista ordenada de tamaños de directorio (que salen en unidades de 1Kb y necesitan desplazamiento antes de convertir) con lo siguiente:

du -d1 | ordenar -n | hr -sK

Si bien du producirá la salida -h, sort no lo ordenará. La adición de -h a las utilidades existentes es un caso clásico de no seguir la filosofía de Unix: tener utilidades simples que realicen trabajos bien definidos.

FJL
fuente
2

Aquí hay una manera de hacerlo casi exclusivamente en bash, solo necesita 'bc' para las matemáticas de coma flotante.

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

Salida:

   1    B
1023    B
   1.00 KiB
  12.06 KiB
 120.56 KiB
   1.18 MiB
  11.77 MiB
Geoffrey
fuente
1
user@host:/usr$ alias duh="du -s -B1 * | sort -g | numfmt --to=iec-i --format='%10f'"
user@host:/usr$ duh

Da:

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

Lamentablemente, no puedo entender cómo obtener la precisión de dos decimales. Probado en Ubuntu 14.04.

Chris
fuente
1

La primera respuesta de @ don_crissti es buena, pero puede ser aún más corta usando Here Strings , p. ej.

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

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

o incluso

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

si <<<no está disponible, puede usar, por ejemplo

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

Existen herramientas de 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

No veo un indicador --binary :(, por lo que tendría que usar python directamente para la representación binaria:

$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
fuente
1

Tuve el mismo problema y rápidamente se me ocurrió una solución simple usando awkla log()función:

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

Y la precisión perdida al usar números flotantes no es tan mala ya que esa precisión se perderá de todos modos.

Bence Kiglics
fuente
0

La respuesta a tu pregunta es sí.

Si bien el formato de salida no se ajusta exactamente a sus especificaciones, la conversión en sí se realiza fácilmente con una herramienta muy estándar (o dos) . A los que me refiero son dcy bc. Puede obtener un informe segmentado alterando sus radices de salida. Me gusta esto:

{   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

Lo uso dcporque es un favorito personal, pero bcpuede hacer lo mismo con una sintaxis diferente y se adhiere a las mismas reglas de formato especificadas por POSIX como:

  • bc obsequiar

    • Para bases mayores de 16, cada dígito se escribirá como un número decimal de varios dígitos separado. Cada dígito, excepto el dígito fraccionario más significativo, irá precedido de un solo espacio . Para bases del 17 al 100, bcdeberá escribir números decimales de dos dígitos; para bases de 101 a 1000, cadenas decimales de tres dígitos, etc. Por ejemplo, el número decimal 1024 en la base 25 se escribiría como:

    01 15 24

    y en base 125, como:

    008 024

mikeserv
fuente
-1

Solución corta y dulce, solo con cáscara:

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
}

No muestra la poción decimal.

El let VAR=expressiones Korn-ish. Sustitúyalo VAR=$(( expression ))por Born-again-ish.

Johan
fuente
Esta solución introduce una tonelada de error ya que el / 1024 siempre se redondea, estoy seguro de que no desea redondear 1.5 TiB a 2 TiB.
Geoffrey
-2

AFAIK no existe una herramienta estándar a la que pueda pasar texto y devuelve una forma legible por humanos. Es posible que pueda encontrar un paquete para realizar dicha tarea para su distribución.

Sin embargo, no entiendo por qué puede necesitar dicha herramienta. La mayoría de los paquetes que dan una salida relacionada, generalmente tienen un interruptor -h o equivalente para una salida legible por humanos.

darnir
fuente
1
Para propósitos de comprensión: Legible por humanos significa justamente eso; legible por humanos. Las diferentes unidades de tamaño que muestran las herramientas que menciona no están destinadas a cálculos programáticos, para los cuales es esencial la uniformidad de las unidades. Trabajar con bytes, que siempre son enteros, es la única forma en que bash puede hacer cualquier cálculo con ellos. Entonces ... calcule en Bytes ... informe a Humano , por ejemplo. "Está a punto de eliminar permanentemente 3 archivos, por un total de 2.44 GiB. ¿Continuar?
Peter.O
Creo que esto debería ser parte de tu pregunta. Me parece que tienes el problema resuelto. Buena suerte.
shellter
1
Una aplicación común es generar números de bytes para la clasificación y convertirlos en unidades legibles por humanos después de la clasificación.
Gilles