Портативный способ получить размер файла (в байтах) в оболочке? - PullRequest
102 голосов
/ 29 ноября 2009

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

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

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

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

или даже:

ls -nl FILE | awk '{print $5}'

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

Ответы [ 14 ]

190 голосов
/ 29 ноября 2009

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

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

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

35 голосов
/ 10 марта 2011

В итоге я написал свою собственную программу (очень маленькую) для отображения только размера. Больше информации здесь: 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.

23 голосов
/ 28 октября 2011

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

du -b FILE

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

13 голосов
/ 29 ноября 2009

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

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

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

8 голосов
/ 20 марта 2013

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

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

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

При необходимости упростите аргументы ls и настройте смещение в $ {__ ln [3]}.

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

7 голосов
/ 02 февраля 2017

BSD имеют stat с опциями, отличными от GNU coreutils 1, но схожими возможностями.

stat -f %z <file name> 

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

4 голосов
/ 21 октября 2016

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

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

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

3 голосов
/ 25 сентября 2015

Вы можете использовать команду 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~
3 голосов
/ 29 ноября 2009

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

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

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

2 голосов
/ 29 ноября 2009

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

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

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

...