Стандартний інструмент для перетворення підрахунку байтів у людський KiB MiB тощо; як дю, ls1


94

Чи є стандартний інструмент, який перетворює ціле число байтів у читабельний для людини кількість найбільшого можливого розміру одиниці, зберігаючи числове значення між 1,00 та 1023,99?

У мене є власний скрипт bash / awk, але я шукаю стандартний інструмент, який можна знайти в багатьох / більшості дистрибутивів ... щось більш загальнодоступне, і в ідеалі є прості аргументи командного рядка та / або можуть приймати конвеєрний ввід.

Ось кілька прикладів типу вихідних даних, які я шукаю.

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

Ось байт-людський скрипт (використовується для вищезазначеного результату)

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

Оновлення  Ось модифікована версія сценарію Жиля , як описано в коментарі до його відповіді .. (модифікована відповідно до мого бажаного вигляду).

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

4
Схоже, тут у нас є нова standard toolу створенні :)
Gowtham

@Gowtham - ваше бажання, можливо, здійснилося! Дивіться мою відповідь нижче або blog.frankleonhardt.com/2015/…
FJL

Зауважте, що останні два суфікси замінені; Yottabyte насправді більший, ніж Zettabyte.
staticfloat

Відповіді:


89

Ні, такого стандартного інструменту немає.

Оскільки GNU coreutils 8.21 (лютий 2013 р., Поки що не присутній у всіх дистрибутивах), для невбудованих Linux та Cygwin ви можете використовувати numfmt. Він не створює абсолютно такий самий вихідний формат (як для Coreutils 8.23, я не думаю, що ви можете отримати дві цифри після десяткових знаків).

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

Багато старих інструментів GNU можуть створювати цей формат, і сортування GNU може сортувати числа за одиницями, починаючи з coreutils 7.5 (серпень 2009 року, настільки присутні у сучасних невбудованих дистрибутивах Linux).


Я вважаю ваш код трохи заплутаним. Ось більш чиста версія awk (вихідний формат не зовсім однаковий):

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

( Повідомлено з більш спеціалізованого питання )


Добре, дякую. Щодо твого сценарію, мені це дуже подобається. Є кілька речей, які привернули мою увагу: (1) var sповинен бути провідним B. Також цей рядок легко змінюється на IEC Binary notation. (2) Він пропускає діапазон 1000-1023 на користь 1 <наступний розмір> (легко змінюється) (3) У нього немає десяткових значень (чого я хочу). Знову це легко змінити. Під час відображення 2 знаків після коми, %fформат викликає значення a round-up<<next size> для значень 1019-1023 ; але вирішувати це не варто. Я опублікував модифіковану версію у своїй відповіді, для загального ознайомлення.
Пітер.О

gnumfmt для користувачів домашніх програм osx, що використовують coreutils
докладно

Для тих, хто хоче перетворити duномери у формат, прочитаний людиною, зауважте, що вам може знадобитися додати --block-size=1до duкоманди.
pawamoy

68

За ст. 8.21, coreutilsВключає в себе numfmt:

numfmtчитає номери в різних представленнях і переформатує їх за потребою.
Найбільш поширене використання - це перетворення чисел на / із представлення людини .

напр

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

ТУТ представлені різні інші приклади (включаючи фільтрування, обробку вводу / виводу тощо) .


Крім того, станом на coreutilsст. 8.24, numfmtМоже обробляти кілька полів з характеристиками діапазону поля , подібних cut, і підтримує налаштування вихідний точності з --formatопцією ,
наприклад ,

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

numfmt - це нещодавно доданий інструмент до пакету coreutils від coreutils-8.21 і далі.
Зама питання

1
Тепер це має бути прийнятою відповіддю.
Енді Фостер

23

Ось варіант, що не стосується лише удару, без bcбудь-яких інших невбудованих елементів, + десяткового формату та двійкових одиниць.

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

Приклади:

$ bytesToHuman 123456789
117.73 MiB

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

$ bytesToHuman 
0 Bytes

Має добре працювати на будь-якій версії Bash там (включаючи MSYSGit's Bash для Windows).


Це найкраща відповідь на мої потреби в басі. На жаль, він розміщений через 1/2 десятиліття після дати проведення ОП, тобто, щоб просунути список голосування, знадобиться певний час.
WinEunuuchs2Unix

@ WinEunuuchs2Unix дякую, я радий, що тобі було корисно :)
Camilo Martin

Зауважте, що останні два суфікси замінені; Yottabyte насправді більший, ніж Zettabyte.
staticfloat

6

Це повне перезапис, натхнене модифікованою версією сценарію awk Гілла Peter.O.

Зміни:

  • Виправляє помилку Peter.O, де він шукає рядок> 1 символу, де йому слід шукати один> 4 символи. Через цю помилку його код не працює для підрозділів ZiB.
  • Вилучає дуже потворне жорстке кодування з довгих рядків розмірів одиниць, розділених пробілом.
  • Додає перемикачі командного рядка, щоб увімкнути / вимкнути прокладку.
  • Додає перемикачі командного рядка для переходу від позначення base-1024 (KiB) до базової-1000 (KB).
  • Обертає все це у простій у використанні функції.
  • Я розміщую це у відкритому доступі та вітаю широке використання.

Код:

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

Тестові випадки (якщо ви хочете подивитися на вихід):

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

Насолоджуйтесь!


5

У perlCPAN є кілька модулів: Формат :: Людина :: Байти та Число :: Байти :: Людський , останній трохи більш повний:

$ 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

І зворотне:

$ 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

ПРИМІТКА: функція parse_bytes()була додана у версії 0.09 (2013-03-01)


5

Через linux - чи існує калькулятор командного рядка для обчислення байтів? - Переповнення стека , я знайшов про GNU Units - хоча без прикладів на сторінці SO; і, як я цього не бачив тут, тут невелика примітка.

По-перше, перевірте, чи є прилади:

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

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

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

Зважаючи на те, що вони є, зробіть printfспецифікатори перетворення формату, щоб форматувати числовий результат:

$ 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

3

Насправді є утиліта, яка робить саме це. Я знаю, тому що це я написав. Він був написаний для * BSD, але його слід компілювати в Linux, якщо у вас є бібліотеки BSD (які, на мою думку, є загальними).

Щойно я випустив нову версію, розміщену тут:

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

Це називається hr, і він займе stdin (або файли) і перетворить номери в читаний для людини формат таким чином, який (зараз) точно такий же, як ls -h і так далі, і він може вибирати окремі канали в рядках, масштабі попередньо масштабовані одиниці (наприклад, якщо вони знаходяться в 512-байтових блоках, перетворюють їх на Mb тощо), коригують набивання стовпців тощо.

Я написав це кілька років тому, тому що думав, що спроба написати сценарій оболонки, хоча й інтелектуально цікавий, також була цілком божевіллям.

Наприклад, використовуючи hr, ви можете легко отримати відсортований список розмірів каталогів (які виходять в одиницях 1 Кбіт і потребують зміщення перед перетворенням) за допомогою наступного:

du -d1 | сортувати -n | ч -ск

У той час як du буде виробляти -h вихід, сортування не буде сортувати за ним. Додавання -h до існуючих утиліт - це класичний випадок не дотримання філософії Unix: мати прості утиліти, які дійсно добре виконують визначені завдання.


2

Ось спосіб зробити це майже чисто в баші, просто потрібен 'bc' для математики з плаваючою точкою.

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
}

Використання:

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

Вихід:

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

1
user@host:/usr$ alias duh="du -s -B1 * | sort -g | numfmt --to=iec-i --format='%10f'"
user@host:/usr$ duh

Дає:

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

На жаль, я не можу зрозуміти, як отримати точність двох десяткових знаків. Тестовано на Ubuntu 14.04.


1

Перша відповідь @ don_crissti хороша, але може бути ще коротшою, використовуючи тут рядки , наприклад

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

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

або навіть

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

якщо <<<немає, ви можете використовувати напр

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

1

Інструменти 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

Я не бачу - бінарного прапора :(, тому вам доведеться використовувати python безпосередньо для двійкового представлення:

$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

У мене була така ж проблема, і я швидко придумав просте рішення за допомогою функції awk' log():

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

А точність, втрачена при використанні плаваючих чисел, не така вже й погана, оскільки ця точність все одно буде втрачена.


0

Відповідь на ваше запитання - так.

Незважаючи на те, що вихідний формат не відповідає саме вашим характеристикам, саме перетворення легко проводиться дуже стандартним інструментом (або двома) . Ті, на які я посилаюсь, є dcі bc. Ви можете отримати сегментований звіт, змінивши їхні вихідні діапазони. Подобається це:

{   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

... які друкує ...

 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

Я використовую dcвище, тому що це особистий фаворит, але bcможе робити те ж саме з різним синтаксисом і дотримується тих же правил формату, що визначені POSIX, як:

  • bc обасе

    • Для основ, що перевищують 16, кожна цифра записується як окреме багатоцифрове десяткове число. Кожній цифрі, крім найбільш значної дробової цифри, передує один пробіл . На базах від 17 до 100 слід bcзаписати двоцифрові десяткові числа; для основ від 101 до 1000, трицифрових десяткових рядків тощо. Наприклад, десяткове число 1024 в базі 25 записується як:

    01 15 24

    і в базі 125, як:

    008 024


-1

Короткий і солодкий, тільки шкаралупа:

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
}

У ньому не відображається десяткове зілля.

let VAR=expressionЄ Корн-Робочі. Замініть VAR=$(( expression ))на Борн-знову-іш.


Це рішення вводить тону помилок, оскільки / 1024 завжди турується, я впевнений, що ви не хочете округляти 1,5 TiB до 2 TiB.
Джеффрі

-2

AFAIK не існує такого стандартного інструменту, до якого можна передати текст, і він повертає читану людиною форму. Можливо, ви зможете знайти пакет для виконання зазначеного завдання для вашого дистрибутива.

Однак я не розумію, навіщо вам може знадобитися такий інструмент. Більшість пакетів, які дають відповідні вихідні дані, зазвичай мають -h або еквівалентний перемикач для зчитуваних з людиною результатів.


1
З метою розуміння: читабельний для людини означає саме це; читабельна людьми. Різні різні одиниці розміру, показані інструментами, які ви згадуєте, не призначені для програмних обчислень, для яких рівномірність одиниць є важливою. Робота з байтами, які завжди є цілими числами, - це єдиний спосіб, коли bash може робити з ними будь-яку арифметику. Отже ... обчислюємо в байтах ... звітуємо в людині , наприклад. "Ви збираєтесь назавжди видалити 3 файли загальною кількістю 2,44 Гб. Продовжити?
Peter.O

Я думаю, це має бути частиною вашого питання. Мені здається, що ви вирішили проблему. Удачі.
обстріл

1
Поширене додаток - це генерувати кількість байтів для сортування та перетворювати в читані людиною одиниці після сортування.
Жиль
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.