Портативный способ получить размер файла (в байтах) в оболочке?


на Linux, я использую stat --format="%s" FILE, но Solaris, к которому у меня есть доступ, не имеет команды stat. Что я должен использовать тогда?

Я пишу сценарии Bash и не могу установить новое программное обеспечение в системе.

Я уже рассматривал возможность использования:

perl -e '@x=stat(shift);print $x[7]' FILE

или еще:

ls -nl FILE | awk '{print }'

но ни один из них не выглядит разумным - запуск Perl только для получения размера файла? Или запуск 2 команд, чтобы сделать то же самое?

15 91

15 ответов:

wc -c < filename (сокращение от слова count,-c выводит количество байтов) является портативным, POSIX решение. Только формат вывода может быть неравномерным на разных платформах, поскольку некоторые пробелы могут быть добавлены (что имеет место для Solaris).

не пропустите перенаправление ввода. Когда файл передается в качестве аргумента, имя файла печатается после числа байтов.

Я беспокоился, что он не будет работать для двоичных файлов, но он работает нормально как на Linux, так и на Solaris. Вы можете попробовать его с wc -c < /usr/bin/wc. Кроме того, утилиты POSIX являются гарантированно обрабатывать двоичные файлы, если иное не указано явно.

Я закончил тем, что написал свою собственную программу (очень маленькую), чтобы отобразить только размер. Более подробная информация здесь: http://fwhacking.blogspot.com/2011/03/bfsize-print-file-size-in-bytes-and.html

два самых чистых способа, на мой взгляд, с общими инструментами Linux:

$ stat -c %s /usr/bin/stat
50000

$ wc -c < /usr/bin/wc
36912

но я просто не хочу вводить параметры или передавать вывод только для получения размера файла, поэтому я использую свой собственный bfsize.

хотя du обычно печатает использование диска, а не фактический размер данных, GNU coreutils du может печатать "видимый размер" файла в байтах:

du -b FILE

но он не будет работать под BSD, Solaris, macOS, ...

наконец я решил использовать ls, и расширение массива bash:

TEMP=( $( ls -ln FILE ) )
SIZE=${TEMP[4]}

это не очень приятно, но, по крайней мере, он делает только 1 fork+execve, и он не зависит от вторичного языка программирования (perl/ruby/python/whatever)

кросс-платформенное самое быстрое решение (использует только одну вилку () для ls, не пытается подсчитать фактические символы, не порождает ненужные awk, perl и т. д.).

протестировано на MacOS, Linux-может потребоваться незначительная модификация для Solaris:

__ln=( $( ls -Lon "" ) )
__size=${__ln[3]}
echo "Size is: $__size bytes"

при необходимости, упростить ls Аргументы и отрегулируйте смещение в ${__ln[3]}.

Примечание: будет следовать символические ссылки.

У BSD есть stat с различными опциями от GNU coreutils один, но аналогичные возможности.

stat -f %z <file name> 

это работает на macOS (проверено на 10.12), FreeBSD, NetBSD и OpenBSD.

можно использовать find команда для получения некоторого набора файлов (здесь извлекаются временные файлы). Тогда вы можете использовать du команда, чтобы получить размер файла каждого файла в удобочитаемой форме с помощью -h переключатель.

find $HOME -type f -name "*~" -exec du -h {} \;

выход:

4.0K    /home/turing/Desktop/JavaExmp/TwoButtons.java~
4.0K    /home/turing/Desktop/JavaExmp/MyDrawPanel.java~
4.0K    /home/turing/Desktop/JavaExmp/Instream.java~
4.0K    /home/turing/Desktop/JavaExmp/RandomDemo.java~
4.0K    /home/turing/Desktop/JavaExmp/Buff.java~
4.0K    /home/turing/Desktop/JavaExmp/SimpleGui2.java~

если вы используете find из GNU fileutils:

size=$( find . -maxdepth 1 -type f -name filename -printf '%s' )

к сожалению, другие реализации find обычно не поддерживают -maxdepth, ни -printf. Это относится, например, к Solaris и macOS find.

при обработке ls -n вывод, в качестве альтернативы плохо переносимым массивам оболочки, вы можете использовать позиционные аргументы, которые образуют единственный массив и являются единственными локальными переменными в стандартной оболочке. Оберните перезапись позиционных аргументов в функцию, чтобы сохранить исходные аргументы для вашего скрипта или функции.

getsize() { set -- $(ls -dn "") && echo ; }
getsize FILE

это разбивает вывод ln -dn в соответствии с текущим IFS настройки переменных окружения, присваивает их позиционным Аргументам и Эхо-сигналам пятый один. Элемент -d гарантирует, что каталоги обрабатываются правильно и -n гарантирует, что имена пользователей и групп не должны быть разрешены, в отличие от-l. Кроме того, имена пользователей и групп, содержащие пробелы, теоретически могут нарушить ожидаемую структуру строк; они обычно запрещены, но эта возможность все еще заставляет программиста остановиться и подумать.

вы первый пример Perl не выглядит необоснованным для меня.

именно по таким причинам я перешел от написания сценариев оболочки (в bash / sh и т. д.) для написания всех, кроме самых тривиальных скриптов в Perl. Я обнаружил, что мне приходилось запускать Perl для определенных требований, и поскольку я делал это все больше и больше, я понял, что написание скриптов в Perl, вероятно, было более мощным (с точки зрения языка и широкого спектра библиотек, доступных через CPAN) и более эффективный способ добиться того, чего хотел.

обратите внимание, что другие языки сценариев оболочки (например, python/ruby), несомненно, будут иметь аналогичные возможности, и вы можете оценить их для своих целей. Я обсуждаю только Perl, так как это язык, который я использую и знаком с ним.

а как же du -s <file> ?

Если у вас есть Perl на вашем Солярисе, то используйте его. В противном случае, ls с awk-это ваш следующий лучший выбор, так как у вас нет stat или ваша находка не GNU find.

в Solaris есть трюк, который я использовал, если вы просите размер более одного файла, он возвращает только общий размер без имен - поэтому включите пустой файл, например /dev / null в качестве второго файла:

например команда fileyouwant/dev / null

Я не могу вспомнить, какой размер команды это работает для ls/wc / etc - к сожалению, у меня нет solaris box, чтобы проверить его.

в Linux вы можете использовать du -h $FILE, Это тоже работает на Солярисе?

вы пробовали du-ks | awk '{print $1*1024}'. Это может сработать.