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

94

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 d'unité possible, tout en maintenant la valeur numérique entre 1,00 et 1023,99?

J'ai mon propre script bash / awk, mais je recherche un outil standard , que l'on trouve sur de nombreuses / la plupart des distributions ... quelque chose de plus disponible, et idéalement avec de simples arguments en ligne de commande, et / ou pouvant accepter une entrée dérivé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 octets-humain (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 ) 
   }'

Mise à jour  Voici une version modifiée du script de Gilles , telle que décrite dans un commentaire à sa réponse. (Modifiée pour correspondre à 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}'
Peter.O
la source
4
Il semble qu'ici nous avons une nouvelle standard toolen train de se faire :)
Gowtham
@Gowtham - votre souhait peut être devenu réalité! Voir ma réponse ci-dessous ou blog.frankleonhardt.com/2015/…
FJL
Notez que les deux derniers suffixes sont échangés; un Yottabyte est en réalité plus grand qu'un Zettabyte.
staticfloat

Réponses:

89

Non, il n'y a pas d'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 la virgule décimale).

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

De nombreux outils GNU plus anciens peuvent produire ce format et le tri GNU peut trier les nombres en unités depuis coreutils 7.5 (août 2009, si présent sur les distributions Linux non intégrées modernes).


Je trouve votre code un peu compliqué. Voici une version plus claire d'awk (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é d'une question plus spécialisée )

Gilles
la source
D'accord merci. En ce qui concerne votre script, je l’aime en gros. Quelques éléments ont attiré mon attention: (1) var sdevrait être en tête B. De plus, cette chaîne est facilement remplacée par la notation binaire IEC. (2) Il saute la plage 1000-1023 en faveur de 1 <taille suivante> (facilement modifiable) (3) Il n’a pas de valeur décimale (ce que je veux). Encore une fois cela est facilement changé. Lorsqu’il affiche 2 décimales, le %fformat entraîne round-upl’a <taille suivante> pour les valeurs 1019-1023 ; mais cela ne vaut pas une solution de contournement .. J'ai posté une version modifiée dans ma réponse, pour référence générale.
Peter.O
gnumfmt pour les utilisateurs d'osx homebrew utilisant coreutils
verboze
Pour ceux qui souhaitent convertir des dunombres en format lisible par l'homme, notez qu'il peut être nécessaire d'ajouter --block-size=1à la ducommande.
pawamoy
68

À partir de v. 8.21, coreutilsComprend numfmt:

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

par exemple

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

Divers autres exemples (y compris le filtrage, le traitement des entrées / sorties, etc.) sont présentés ICI .


En outre, à partir de coreutilsv. 8.24, numfmtPeut traiter plusieurs champs avec des spécifications de plage de champs similaires à cut, et prend en charge le réglage de la précision de sortie avec l' --formatoption,
par exemple

numfmt --to=iec-i --field=2,4 --format='%.3f' <<<'tx: 180000 rx: 2000000'
tx: 175.782Ki rx: 1.908Mi
don_crissti
la source
numfmt est un outil nouvellement ajouté au paquet coreutils à partir de coreutils-8.21.
Zama Ques
1
Cela devrait maintenant être la réponse acceptée.
Andy Foster
23

Voici une option uniquement bash, aucun bcou aucun autre élément 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 bien fonctionner sur n'importe quelle version de Bash (y compris Bash de MSYSGit pour Windows).

Camilo Martin
la source
C'est la meilleure réponse pour mes besoins bash. Malheureusement, il est affiché une demi-décennie après la date de l'OP, ce qui signifie qu'il faudra un certain temps pour passer à la liste de vote.
WinEunuuchs2Unix
@ WinEunuuchs2Unix merci, je suis content que cela vous ait aidé :)
Camilo Martin
Notez que les deux derniers suffixes sont échangés; un Yottabyte est en réalité plus grand qu'un Zettabyte.
staticfloat
6

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

Changements:

  • Corrige le bogue de Peter.O qui recherche une chaîne de> 1 caractère où il devrait rechercher un> 4 caractères. En raison de ce bug, son code ne fonctionne pas pour les unités ZiB.
  • Supprime le très dur codage en dur 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 base 1000 (Ko).
  • Le tout dans une fonction facile à utiliser.
  • Je place ceci dans le domaine public et me réjouis d'une 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 le résultat):

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!

John
la source
5

Il y a quelques perlmodules sur CPAN: Format :: Humain :: Octets et Nombre :: Octets :: Humain , le dernier étant 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 a parse_bytes()été ajoutée à la version 0.09 (2013-03-01).

Stéphane Chazelas
la source
5

Via linux - Existe-t-il un calculateur en ligne de commande pour le calcul des octets? - Stack Overflow , j'ai trouvé des informations sur les unités GNU - bien que sans exemples sur la page SO; et comme je ne l'ai pas vu ici, voici une petite note à ce sujet.

Tout d’abord, vérifiez 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'

Etant donné qu'ils le sont, effectuez une conversion - les printfspé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
Sdaau
la source
3

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é sous Linux si vous avez les bibliothèques BSD (que je pense être communes).

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

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

Il s’appelle hr. Il faut stdin (ou fichiers) et convertit les nombres en format lisible par l’homme de la même manière que ls -h, etc., et permet de sélectionner des flux individuels en lignes, à l’échelle les unités pré-dimensionnées (par exemple, si elles sont en blocs de 512 octets, convertissez-les en Mo, etc.), ajustez le remplissage de la colonne, etc.

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

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

du -d1 | trier -n | hr

Bien que du produise une sortie -h, le tri ne triera pas en fonction. L'ajout de -h aux utilitaires existants est un cas classique de non-respect de la philosophie unix: disposer d'utilitaires simples effectuant très bien des tâches définies.

FJL
la source
2

Voici une façon de le faire presque purement en bash, il suffit de «bc» pour le calcul 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

Sortie:

   1    B
1023    B
   1.00 KiB
  12.06 KiB
 120.56 KiB
   1.18 MiB
  11.77 MiB
Geoffrey
la source
1
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 n'arrive pas à comprendre comment obtenir une précision à deux décimales. Testé sur Ubuntu 14.04.

Chris
la source
1

La première réponse de @ don_crissti est bonne, mais peut être encore plus courte avec Here Strings , par exemple

$ 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
craeckie
la source
1

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 devriez donc utiliser directement python 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
ThorSummoner
la source
1

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

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 ça, car cette précision sera perdue de toute façon.

Bence Kiglics
la source
0

La réponse à ta question est oui.

Bien que le format de sortie ne corresponde pas exactement à vos spécifications, la conversion elle-même est facilement réalisée par un outil très standard (ou deux) . Ceux auxquels je me réfère sont dcet bc. Vous pouvez obtenir un rapport segmenté en modifiant leurs radices 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 dcci-dessus parce que c'est un favori personnel, mais bcje peux faire la même chose avec une syntaxe différente et adhérer aux mêmes règles de format que celles spécifiées par POSIX comme:

  • bc obase

    • Pour les bases supérieures à 16, chaque chiffre doit être écrit sous la forme d'un nombre décimal distinct à plusieurs chiffres. Chaque chiffre, à l'exception du chiffre le plus significatif, doit être précédé d'un seul espace . Pour les bases de 17 à 100, bcdoit é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’écrit comme suit:

    01 15 24

    et en base 125, comme:

    008 024

Mikeserv
la source
-1

Solution courte et douce, coquille uniquement:

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
}

Il ne montre pas la potion décimale.

Le let VAR=expressionest Korn-ish. Substitute avec VAR=$(( expression ))pour Born-again-ish.

Johan
la source
Cette solution introduit une tonne d’erreur lorsque le / 1024 arrondit toujours, je suis sûr que vous ne voulez pas arrondir 1,5 TiB à 2 TiB.
Geoffrey
-2

Autant que je sache, il n’existe aucun outil standard de ce type auquel vous pouvez passer du texte et il renvoie un formulaire lisible par l’homme. Vous pourrez peut-être trouver un paquet pour accomplir cette tâche pour votre distribution.

Cependant, je ne comprends pas pourquoi vous pourriez avoir besoin d’un tel outil. La plupart des paquets qui donnent une sortie associée ont généralement un commutateur -h ou équivalent pour une sortie lisible par l'homme.

Darnir
la source
1
Aux fins de la compréhension: lisible par l'homme signifie simplement cela; lisible par les humains. Les différentes unités de taille indiquées par les outils que vous avez mentionnés ne sont pas destinées aux calculs programmatiques, pour lesquels l'uniformité des unités est essentielle. Travailler avec des octets, qui sont toujours des entiers, est la seule façon dont bash peut effectuer des calculs arithmétiques avec eux. Alors ... calculez en octets ... rapportez en humain , par exemple. "Vous êtes sur le point de supprimer définitivement 3 fichiers, pour un total de 2,44 Gio. Continuer?
Peter.O
Je pense que cela devrait faire partie de votre question. Il me semble que le problème est résolu. Bonne chance.
shellter
1
Une application courante consiste à générer un nombre d'octets pour le tri et à les convertir en unités lisibles par l'homme après le tri.
Gilles