Alat standar untuk mengubah byte-count menjadi KiB MiB manusia, dll; seperti du, ls1

94

Apakah ada alat standar yang mengubah bilangan integer Bytes menjadi jumlah yang dapat dibaca manusia dari ukuran unit terbesar yang mungkin, sambil menjaga nilai numerik antara 1,00 dan 1023,99?

Saya memiliki skrip bash / awk saya sendiri, tetapi saya sedang mencari alat standar , yang ditemukan di banyak / kebanyakan distro ... sesuatu yang lebih umum tersedia, dan idealnya memiliki args baris perintah sederhana, dan / atau dapat menerima input pipa.

Berikut adalah beberapa contoh tipe output yang saya cari.

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

Berikut ini skrip byte-manusia (digunakan untuk output di atas)

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

Pembaruan  Berikut ini adalah versi modifikasi dari skrip Gilles , seperti yang dijelaskan dalam komentar atas jawabannya .. (dimodifikasi agar sesuai dengan tampilan yang saya sukai).

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
sumber
4
Sepertinya di sini kita memiliki yang baru standard tooldalam pembuatan :)
Gowtham
@Gowtham - keinginanmu mungkin terwujud! Lihat jawaban saya di bawah ini atau blog.frankleonhardt.com/2015/...
FJL
Perhatikan bahwa dua sufiks terakhir ditukar; Yottabyte sebenarnya lebih besar dari Zettabyte.
staticfloat

Jawaban:

89

Tidak, tidak ada alat standar seperti itu.

Karena GNU coreutils 8.21 (Feb 2013, jadi belum ada di semua distribusi), di Linux dan Cygwin yang tidak tertanam, Anda dapat menggunakannya numfmt. Itu tidak menghasilkan format output yang persis sama (pada coreutils 8.23, saya tidak berpikir Anda bisa mendapatkan 2 digit setelah titik desimal).

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

Banyak alat GNU yang lebih tua dapat menghasilkan format ini dan pengurutan GNU dapat mengurutkan angka dengan unit sejak coreutils 7.5 (Agustus 2009, jadi ada pada distribusi Linux non-embedded modern).


Saya menemukan kode Anda agak berbelit-belit. Ini versi awk yang lebih bersih (format output tidak persis sama):

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

( Di-posting ulang dari pertanyaan yang lebih khusus )

Gilles
sumber
Oke terima kasih. Tentang skrip Anda, pada dasarnya saya sangat menyukainya. Ada beberapa hal yang menarik perhatian saya: (1) var sharus memimpin B. String ini juga mudah diubah menjadi notasi IEC Binary. (2) Melompati rentang 1000-1023 dan mendukung 1 <ukuran berikutnya> (mudah diubah) (3) Tidak memiliki nilai desimal (yang saya inginkan). Sekali lagi ini mudah diubah. Saat menampilkan 2 tempat desimal, %fformat menyebabkan a round-upke <ukuran berikutnya> untuk nilai 1019-1023 ; tapi tidak ada solusinya .. Saya telah mengirim versi yang dimodifikasi dalam jawaban saya, untuk referensi umum.
Peter.O
gnumfmt untuk pengguna homebrew osx menggunakan
coreutils
Bagi mereka yang ingin mengkonversi duangka ke format yang dapat dibaca manusia, perhatikan bahwa Anda mungkin perlu menambahkan --block-size=1untuk duperintah.
pawamoy
68

Pada v. 8.21, coreutilsMeliputi numfmt:

numfmtmembaca angka dalam berbagai representasi dan memformatnya sesuai permintaan.
Penggunaan yang paling umum adalah mengubah angka ke / dari representasi manusia .

misalnya

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

Berbagai contoh lain (termasuk pemfilteran, pemrosesan input / output dll) disajikan di SINI .


Selain itu, pada coreutilsv. 8.24, numfmtDapat memproses beberapa bidang dengan spesifikasi rentang bidang yang mirip dengan cut, dan mendukung pengaturan presisi keluaran dengan --formatopsi
misalnya

numfmt --to=iec-i --field=2,4 --format='%.3f' <<<'tx: 180000 rx: 2000000'
tx: 175.782Ki rx: 1.908Mi
don_crissti
sumber
numfmt adalah alat yang baru ditambahkan ke paket coreutils dari coreutils-8.21 dan seterusnya.
Zama Ques
1
Sekarang ini seharusnya jawaban yang diterima.
Andy Foster
23

Berikut adalah opsi bash-only, tidak ada bcatau non-builtin lainnya, format + desimal dan unit biner.

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

Contoh:

$ bytesToHuman 123456789
117.73 MiB

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

$ bytesToHuman 
0 Bytes

Seharusnya berkinerja baik di semua versi Bash di luar sana (termasuk Bash untuk Windows MSYSGit).

Camilo Martin
sumber
Ini adalah jawaban terbaik untuk kebutuhan bash saya. Sayangnya itu diposting 1/2 dekade setelah tanggal OP yang berarti perlu beberapa saat untuk naik ke daftar pemilih.
WinEunuuchs2Unix
@ WinEunuuchs2Unix terima kasih, saya senang itu membantu Anda :)
Camilo Martin
Perhatikan bahwa dua sufiks terakhir ditukar; Yottabyte sebenarnya lebih besar dari Zettabyte.
staticfloat
6

Ini adalah penulisan ulang lengkap yang terinspirasi oleh skrip awk Gilles yang dimodifikasi versi Peter.O.

Perubahan:

  • Memperbaiki bug Peter.O di mana ia mencari string> 1 karakter di mana ia seharusnya mencari satu> 4 karakter. Karena bug itu, kodenya tidak berfungsi untuk unit ZiB.
  • Menghapus hardcoding yang sangat jelek dari string panjang ukuran unit yang dipisahkan ruang.
  • Tambahkan sakelar baris perintah untuk mengaktifkan / menonaktifkan bantalan.
  • Menambahkan sakelar baris perintah untuk beralih dari notasi base-1024 (KiB) ke base-1000 (KB).
  • Membungkus semuanya dalam fungsi yang mudah digunakan.
  • Saya menempatkan ini dalam domain publik dan menyambut penggunaan yang luas.

Kode:

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

Uji Kasus (jika Anda ingin melihat output):

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

Nikmati!

John
sumber
5

Ada beberapa perlmodul di CPAN: Format :: Manusia :: Bytes dan Number :: Bytes :: Human , yang terakhir menjadi sedikit lebih lengkap:

$ 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

Dan sebaliknya:

$ 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

CATATAN: fungsi parse_bytes()ini ditambahkan dalam versi 0.09 (2013-03-01)

Stéphane Chazelas
sumber
5

Via linux - Apakah ada kalkulator baris perintah untuk perhitungan byte? - Stack Overflow , saya temukan tentang Unit GNU - walaupun tanpa contoh di halaman SO; dan karena saya tidak melihatnya tercantum di sini, ini adalah catatan kecil tentangnya.

Pertama, periksa apakah unit ada:

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

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

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

Karena begitu, lakukan konversi - printfpenentu format diterima untuk memformat hasil numerik:

$ 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
sumber
3

Sebenarnya, ada utilitas yang melakukan hal ini. Aku tahu karena aku yang menulisnya. Itu ditulis untuk * BSD tetapi harus dikompilasi di Linux jika Anda memiliki perpustakaan BSD (yang saya percaya adalah umum).

Saya baru saja merilis versi baru, diposting di sini:

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

Ini disebut jam, dan itu akan mengambil stdin (atau file) dan mengonversi angka ke format yang dapat dibaca manusia dengan cara yang (sekarang) persis sama dengan ls -h dan seterusnya, dan dapat memilih umpan individu dalam garis, skala unit pra-skala (misalnya jika mereka berada di blok 512-byte mengubahnya menjadi Mb dll), sesuaikan bantalan kolom, dan sebagainya.

Saya menulisnya beberapa tahun yang lalu karena saya pikir mencoba menulis skrip shell, meskipun secara intelektual menarik, juga sangat gila.

Menggunakan jam, misalnya, Anda dapat dengan mudah mendapatkan daftar ukuran direktori yang diurutkan (yang keluar dalam satuan 1Kb dan perlu digeser sebelum mengonversi) dengan yang berikut ini:

du -d1 | sort -n | jam -sK

Sementara du akan menghasilkan -h output, sort tidak akan mengurutkannya. Penambahan -h ke utilitas yang ada adalah kasus klasik tidak mengikuti filosofi unix: memiliki utilitas sederhana melakukan pekerjaan yang didefinisikan dengan sangat baik.

FJL
sumber
2

Berikut adalah cara untuk melakukannya hampir murni di bash, hanya perlu 'bc' untuk matematika floating point.

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
}

Pemakaian:

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

Keluaran:

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

Memberi:

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

Sayangnya saya tidak tahu bagaimana cara mendapatkan akurasi dua desimal. Diuji pada Ubuntu 14.04.

Chris
sumber
1

@ don_crissti Jawaban pertama adalah baik, tetapi bisa lebih pendek menggunakan Here Strings , mis

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

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

atau bahkan

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

jika <<<tidak tersedia Anda dapat menggunakan mis

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

Ada alat 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

Saya tidak melihat flag --binary :(, jadi Anda harus menggunakan python langsung untuk representasi biner:

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

Aku punya masalah yang sama dan saya cepat datang dengan solusi sederhana menggunakan awk's log()fungsi:

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

Dan presisi yang hilang dalam menggunakan angka float tidak terlalu buruk karena presisi itu akan hilang.

Bence Kiglics
sumber
0

Jawaban atas pertanyaan Anda adalah ya.

Meskipun format output tidak sesuai dengan spesifikasi Anda, konversi itu sendiri mudah dilakukan oleh alat yang sangat standar (atau dua) . Yang saya maksud adalah dcdan bc. Anda bisa mendapatkan laporan tersegmentasi dengan mengubah radies output mereka. Seperti ini:

{   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

... yang mencetak ...

 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

Saya menggunakan di dcatas karena ini adalah favorit pribadi, tetapi bcdapat melakukan hal yang sama dengan sintaks yang berbeda dan mematuhi aturan format yang sama seperti yang ditentukan oleh POSIX seperti:

  • bc Obase

    • Untuk pangkalan yang lebih besar dari 16, setiap digit harus ditulis sebagai angka desimal multi-digit yang terpisah. Setiap digit kecuali digit pecahan yang paling signifikan harus didahului dengan spasi tunggal . Untuk pangkalan dari 17 hingga 100, bcharus menulis angka desimal dua digit; untuk basis dari 101 hingga 1000, string desimal tiga digit, dan sebagainya. Misalnya, angka desimal 1024 dalam basis 25 akan ditulis sebagai:

    01 15 24

    dan dalam basis 125, sebagai:

    008 024

mikeserv
sumber
-1

Solusi singkat dan manis, hanya shell:

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
}

Itu tidak menunjukkan ramuan desimal.

Ini let VAR=expressionadalah Korn-ish. Pengganti dengan VAR=$(( expression ))untuk Born-again-ish.

Johan
sumber
Solusi ini memperkenalkan satu ton kesalahan karena / 1024 selalu bulat, saya yakin Anda tidak ingin mengumpulkan 1,5 TiB hingga 2 TiB.
Geoffrey
-2

AFAIK tidak ada alat standar yang dapat digunakan untuk mengirim teks dan mengembalikan formulir yang dapat dibaca manusia. Anda mungkin dapat menemukan paket untuk menyelesaikan tugas tersebut untuk distro Anda.

Namun, saya tidak mengerti mengapa Anda membutuhkan alat seperti itu. Sebagian besar paket yang memberikan output terkait, biasanya memiliki saklar -h atau setara untuk output yang dapat dibaca manusia.

darnir
sumber
1
Untuk tujuan pemahaman: Dapat dibaca oleh manusia berarti hanya itu; dibaca oleh manusia. Berbagai unit ukuran yang berbeda yang ditunjukkan oleh alat yang Anda sebutkan tidak dimaksudkan untuk perhitungan terprogram, yang keseragaman unit sangat penting. Bekerja dengan byte, yang selalu bilangan bulat, adalah satu-satunya cara bash dapat melakukan aritmatika dengannya. Jadi ... hitung dalam Bytes ... laporkan ke dalam Human , mis. "Anda akan menghapus 3 file secara permanen, dengan total 2,44 GiB. Lanjutkan?
Peter.O
Saya pikir ini harus menjadi bagian dari pertanyaan Anda. Sepertinya saya sudah menyelesaikan masalah. Semoga berhasil.
kerang
1
Aplikasi umum adalah untuk menghasilkan jumlah byte untuk disortir, dan dikonversi ke unit yang dapat dibaca manusia setelah disortir.
Gilles