Стандартный инструмент для преобразования количества байтов в KiB, MiB и т. Д .; как du, 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  

Вот скрипт bytes-human (используется для вывода выше)

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}'
Peter.O
источник
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, я не думаю, что вы можете получить 2 цифры после десятичной точки).

$ 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. (2) Он пропускает диапазон 1000-1023 в пользу 1 <следующий размер> (легко изменяется) (3) У него нет десятичных значений (что я хочу). Опять же это легко изменить. При отображении 2 десятичных знаков %fформат приводит round-upк значению <следующий размер> для значений 1019-1023 ; но это не стоит обходного пути. Я опубликовал измененную версию в своем ответе для общего ознакомления.
Peter.O
gnumfmt для домашних пользователей osx, использующих coreutils
verboze
Для тех, кто хочет преобразовать duчисла в понятный человеку формат, обратите внимание, что вам может потребоваться добавить --block-size=1в duкоманду.
Павамой
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
don_crissti
источник
numfmt - это недавно добавленный инструмент в пакет coreutils, начиная с coreutils-8.21 и далее.
Зама Кес
1
Теперь это должен быть принятый ответ.
Энди Фостер
23

Вот опция bash-only, нет 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 (включая MSYGit Bash для Windows).

Камило Мартин
источник
Это лучший ответ для моих потребностей Bash. К сожалению, он публикуется спустя 1/2 десятилетия после даты ОП, а это означает, что потребуется время, чтобы подняться вверх по списку для голосования.
WinEunuuchs2Unix
@ WinEunuuchs2Unix спасибо, я рад, что это было полезно для вас :)
Камило Мартин
Обратите внимание, что последние два суффикса поменялись местами; Yottabyte на самом деле больше, чем Zettabyte.
staticfloat
6

Это полное переписывание, вдохновленное измененной версией сценария Жиля Питера.

Изменения:

  • Исправляет ошибку Питера. O, где он ищет строку> 1 символа, где он должен искать один> 4 символа. Из-за этой ошибки его код не работает для модулей ZiB.
  • Удаляет очень уродливое жесткое кодирование длинной строки разделенных пробелами размеров блоков.
  • Добавляет ключи командной строки для включения / отключения заполнения.
  • Добавляет переключатели командной строки для перехода от нотации base-1024 (KiB) к base-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 есть несколько модулей: Format :: Human :: Bytes и Number :: Bytes :: Human , последний из которых немного более полон:

$ 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 - хотя без примеров на странице 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
sdaau
источник
3

На самом деле, есть утилита, которая делает именно это. Я знаю, потому что это я написал. Он был написан для * BSD, но должен компилироваться в Linux, если у вас есть библиотеки BSD (которые, я считаю, распространены).

Я только что выпустил новую версию, размещенную здесь:

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

Он называется hr, и он возьмет stdin (или файлы) и преобразует числа в удобочитаемый формат таким образом, который (сейчас) точно такой же, как ls -h и т. Д., И может выбирать отдельные каналы в строках, масштабировать предварительно масштабированные единицы (например, если они находятся в 512-байтовых блоках, преобразуют их в Мб и т. д.), настраивают заполнение столбцов и т. д.

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

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

du -d1 | сортировать -n | hr -sK

Пока du выдаст -h, sort не будет сортировать по нему. Добавление -h к существующим утилитам является классическим случаем несоблюдения философии Unix: иметь простые утилиты, выполняющие определенные задачи действительно хорошо.

клеенные щиты
источник
2

Вот способ сделать это почти чисто в bash, просто требуется '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 хорош, но может быть еще короче, используя Here Strings , например

$ 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
craeckie
источник
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

Я не вижу флага --binary :(, поэтому вам придется использовать 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
ThorSummoner
источник
1

У меня была такая же проблема , и я быстро придумал простое решение с использованием awk«s 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 obase

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

    01 15 24

    и в базе 125, как:

    008 024

mikeserv
источник
-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 ))на Born-again-ish.

Johan
источник
Это решение вносит массу ошибок, поскольку / 1024 всегда округляется, я уверен, что вы не хотите округлять 1,5 ТиБ до 2 ТиБ.
Джеффри
-2

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

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

darnir
источник
1
В целях понимания: человекочитаемый означает только это; читаемый людьми. Различные единицы измерения, показанные упомянутыми вами инструментами, не предназначены для программных вычислений, для которых важна однородность единиц. Работа с байтами, которые всегда являются целыми числами, является единственным способом, которым bash может выполнять с ними любую арифметику. Итак ... вычислить в байтах ... сообщить в человека , например. «Вы собираетесь навсегда удалить 3 файла общим объемом 2,44 ГиБ. Продолжить?
Peter.O
Я думаю, что это должно быть частью вашего вопроса. Похоже, ты решил проблему. Удачи.
Shellter
1
Распространенным применением является генерация количества байтов для сортировки и преобразование в удобочитаемые единицы после сортировки.
Жиль