web-dev-qa-db-fra.com

Un outil standard pour convertir un décompte d'octets en KiB MiB humain, etc. comme du, ls1

Existe-t-il un outil standard qui convertit un nombre entier d'octets en un nombre lisible par l'homme de la plus grande taille unitaire possible, tout en conservant la valeur numérique entre 1,00 et 1023,99?

J'ai mon propre script bash/awk, mais je recherche un outil standard, qui se trouve sur de nombreuses/la plupart des distributions ... quelque chose de plus généralement disponible, et idéalement avec des arguments de ligne de commande simples, et/ou peut accepter une entrée canalisée.

Voici quelques exemples du type de sortie que je recherche.

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

Voici le script bytes-human (utilisé pour la sortie ci-dessus)

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

pdate Voici une version modifiée du script Gilles ', comme décrit dans un commentaire à sa réponse .. (modifié pour convenir à mon look préféré).

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}'
106
Peter.O

Non, il n'y a pas un tel outil standard.

Depuis GNU coreutils 8.21 (février 2013, donc pas encore présent dans toutes les distributions), sur Linux non intégré et Cygwin, vous pouvez utiliser numfmt Il ne produit pas exactement le même format de sortie (à partir de coreutils 8.23, je ne pense pas que vous puissiez obtenir 2 chiffres après les décimales).

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

Beaucoup d'anciens GNU peuvent produire ce format et le tri GNU peut trier les nombres avec des unités depuis coreutils 7.5 (août 2009, donc présent sur les distributions Linux modernes non intégrées).


Je trouve votre code un peu compliqué. Voici une version awk plus propre (le format de sortie n'est pas exactement identique):

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

( Republié à partir d'une question plus spécialisée )

À partir du v. 8.21, coreutils comprend numfmt :

numfmt lit les nombres dans diverses représentations et les reformate comme demandé.
L'utilisation la plus courante consiste à convertir des nombres en/à partir de représentation humaine .

par exemple.

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

Divers autres exemples (y compris le filtrage, le traitement d'entrée/sortie, etc.) sont présentés ICI .


De plus, à partir de coreutils v. 8.24, numfmt peut traiter plusieurs champs avec des spécifications de plage de champs similaires à cut et prend en charge la définition de la précision de sortie avec le --format option
par exemple.

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

Voici une option bash uniquement, pas de bc ou tout autre non intégré, + format décimal et unités binaires.

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

Exemples:

$ bytesToHuman 123456789
117.73 MiB

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

$ bytesToHuman 
0 Bytes

Devrait fonctionner correctement sur n'importe quelle version de Bash (y compris Bash pour Windows de MSYSGit).

29
Camilo Martin

Via linux - Existe-t-il une calculatrice en ligne de commande pour les calculs d'octets? - Débordement de pile , j'ai trouvé environ nités GN - mais sans exemples sur la page SO; et comme je ne l'ai pas vu répertorié ici, voici une petite note à ce sujet.

Vérifiez d'abord si les unités sont présentes:

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

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

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

Étant donné qu'ils le sont, faites une conversion - printf les spécificateurs de format sont acceptés pour formater le résultat numérique:

$ 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
7
sdaau

Il s'agit d'une réécriture complète inspirée de la version modifiée de Peter.O du script awk de Gilles.

Changements:

  • Corrige le bug de Peter.O où il cherche une chaîne de> 1 caractère où il devrait chercher un> 4 caractères. En raison de ce bogue, son code ne fonctionne pas pour les unités ZiB.
  • Supprime le codage en dur très laid d'une longue chaîne de tailles d'unités séparées par des espaces.
  • Ajoute des commutateurs de ligne de commande pour activer/désactiver le remplissage.
  • Ajoute des commutateurs de ligne de commande pour passer de la notation base-1024 (KiB) à la notation base-1000 (KB).
  • Enveloppe le tout dans une fonction facile à utiliser.
  • Je mets cela dans le domaine public et j'en salue l'utilisation généralisée.

Code:

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

Cas de test (si vous voulez regarder la sortie):

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

Prendre plaisir!

6
John

Il y a quelques modules Perl sur CPAN: Format :: Human :: Bytes et Number :: Bytes :: Human , ce dernier étant un un peu plus complet:

$ 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

Et l'inverse:

$ 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

REMARQUE: la fonction parse_bytes() était ajoutée dans la version 0.09 (2013-03-01)

5
Stéphane Chazelas

En fait, il existe un utilitaire qui fait exactement cela. Je sais que c'est moi qui l'ai écrit. Il a été écrit pour * BSD mais devrait être compilé sur Linux si vous avez les bibliothèques BSD (qui je pense sont courantes).

Je viens de publier une nouvelle version, publiée ici:

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

Cela s'appelle hr, et cela prendra stdin (ou fichiers) et convertira les nombres au format lisible par l'homme d'une manière qui est (maintenant) exactement la même que ls -h et ainsi de suite, et il peut sélectionner des flux individuels en lignes, à l'échelle unités pré-mises à l'échelle (par exemple, si elles sont dans des blocs de 512 octets, convertissez-les en Mo, etc.), ajustez le remplissage des colonnes, etc.

Je l'ai écrit il y a quelques années parce que je pensais qu'écrire un script Shell, bien qu'intellectuellement intéressant, était aussi une folie totale.

En utilisant hr, par exemple, vous pouvez facilement obtenir une liste triée des tailles de répertoire (qui sortent en unités de 1 Ko et doivent être décalées avant la conversion) avec les éléments suivants:

du -d1 | sort -n | hr -sK

Alors que du produira une sortie -h, le tri ne sera pas trié par lui. L'ajout de -h aux utilitaires existants est un cas classique de non-respect de la philosophie unix: avoir des utilitaires simples faisant très bien les tâches définies.

3
FJL

Voici un moyen de le faire presque uniquement en bash, il suffit de 'bc' pour les mathématiques en virgule flottante.

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
}

Usage:

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

Production:

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

J'ai eu le même problème et j'ai rapidement trouvé une solution simple en utilisant la fonction log() de awk:

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

Et la précision perdue en utilisant des nombres flottants n'est pas si mauvaise que cette précision sera de toute façon perdue.

2
Bence Kiglics

La première réponse de @ don_crissti est bonne, mais peut être encore plus courte en utilisant Here Strings , par ex.

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

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

ou même

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

si <<< n'est pas disponible, vous pouvez utiliser par exemple.

$ echo "1234567" | numfmt --to=iec-i --suffix=B
1.2MiB
1
craeckie
user@Host:/usr$ alias duh="du -s -B1 * | sort -g | numfmt --to=iec-i --format='%10f'"
user@Host:/usr$ duh

Donne:

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

Malheureusement, je ne sais pas comment obtenir une précision de deux décimales. Testé sur Ubuntu 14.04.

1
Chris

Les outils Python existent

$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

Je ne vois pas d'indicateur --binary :(, vous devez donc utiliser python directement pour la représentation binaire:

$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
ThorSummoner

La réponse à ta question est oui.

Bien que le format de sortie ne soit pas exactement conforme à vos spécifications, la conversion elle-même est facilement effectuée par un outil très standard (ou deux) . Ceux auxquels je me réfère sont dc et bc. Vous pouvez obtenir un rapport segmenté en modifiant leurs rayons de sortie. Comme ça:

{   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

... qui 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

J'utilise dc ci-dessus parce que c'est un favori personnel, mais bc peut faire la même chose avec une syntaxe différente et adhère aux mêmes règles de format que spécifié par POSIX comme:

  • bc obase

    • Pour les bases supérieures à 16, chaque chiffre doit être écrit sous la forme d'un nombre décimal à plusieurs chiffres distinct. Chaque chiffre, à l'exception du chiffre fractionnaire le plus significatif, doit être précédé d'un seul espace . Pour les bases de 17 à 100, bc doit écrire des nombres décimaux à deux chiffres; pour les bases de 101 à 1000, chaînes décimales à trois chiffres, etc. Par exemple, le nombre décimal 1024 en base 25 s'écrirait comme suit:

    01 15 24

    et en base 125, comme:

    008 024

0
mikeserv

pip install humanfriendly puis ajoutez une fonction simple à votre shell par défaut (par exemple ~/.bashrc)

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

Utilisez comme ça

➜ fsize file.txt
6.17 KB
0
Most Wanted

Si vous pouvez utiliser Python et pip, vous pouvez résoudre ce problème avec humanize . (Merci à Pyrocater pour l'idée .))

$ 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
0
Bryan Roach