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}'
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}'
À 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
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).
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
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:
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!
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)
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.
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
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.
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
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.
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
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
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
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
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