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


121

В 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 команды, чтобы сделать то же самое?


1
ну, сценарий bash - это программное обеспечение, и если вы можете поместить его в систему, вы можете установить программное обеспечение.
просто кто-нибудь

4
Технически - правда. Я имел в виду, что у меня нет прав root и я не могу устанавливать новые пакеты. Конечно, установка в домашнем каталоге возможна. Но не совсем так, когда мне нужно сделать переносимый сценарий и установить на «X» машины, новые дополнительные пакеты становятся сложными.

Ответы:


207

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

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

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


67
Или просто, wc -c < fileесли вы не хотите, чтобы имя файла отображалось.
caf

34
Однако, если я не ошибаюсь, wcв конвейере должен read()весь поток считать байты. В ls/ awkрешения (и подобные) используют системный вызов , чтобы получить размер, который должен быть линейное время ( по сравнению с O (размер))
jmtd

1
Я помню, wcкак в последний раз я делал это на полном жестком диске очень медленно. Это было достаточно медленно, чтобы я мог переписать сценарий до того, как закончил первый, пришел сюда, чтобы вспомнить, как я это делал, лол.
Камило Мартин

6
Я бы не стал использовать wc -c; выглядит намного аккуратнее, но ls+ awkлучше по скорости / использованию ресурсов. Кроме того, я просто хотел указать, что вам действительно нужно wcпостобработать результаты, потому что в некоторых системах перед результатом будут пробелы, которые вам может потребоваться удалить, прежде чем вы сможете проводить сравнения.
Haravikk

3
wc -cотлично, но он не будет работать, если у вас нет доступа для чтения к файлу.
Сайлас

41

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


2
В первой строке описания проблемы указано, что stat не является вариантом, а wc -c является лучшим ответом уже более года, поэтому я не уверен, в чем смысл этого ответа.

22
Дело в людях вроде меня, которые находят этот ТАК вопрос в Google и stat являются для них вариантом.
yo '22

3
Я работаю над встроенной системой, которая wc -cтребует 4090 мсек для файла размером 10 МБ против «0» мсек для stat -c %s, поэтому я согласен, что полезно иметь альтернативные решения, даже если они не дают точного ответа на поставленный вопрос.
Роберт Калхун

3
"stat -c" не переносится / не принимает те же аргументы в MacOS, что и в Linux. "wc -c" будет очень медленным для больших файлов.
Orwellophile

2
stat тоже не переносится. stat -c %s /usr/bin/stat stat: illegal option -- c usage: stat [-FlLnqrsx] [-f format] [-t timefmt] [file ...]

27

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

du -b FILE

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


3
На MacOS X brew install coreutilsи gdu -bбудет тот же эффект
Хосе Альбан

1
Я предпочитаю этот метод, потому что wcдля получения результата необходимо duнемедленно прочитать весь файл .
CousinCocaine 02

2
POSIX упоминает du -bв совершенно другом контексте в duобосновании .
Palec 04

При этом используется только lstatвызов, поэтому его производительность не зависит от размера файла. Короче stat -c '%s', но менее интуитивно понятен и работает по-разному для папок (печатает размер каждого файла внутри).
Palec 04

FreeBSDdu может приблизиться к использованию du -A -B1, но она по-прежнему выводит результат, кратный 1024B блокам. Не удалось заставить его печатать количество байтов. Даже установка BLOCKSIZE=1в environemnt не помогает, потому что тогда используется блок 512B.
Palec 04

13

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

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

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


Небольшое отступление - "l" в "-ln" не требуется; '-n' в точности совпадает с '-ln'
запрещен

Нет, это не так. Просто сравните выходы.

1
Можно было бы предположить, что портативному устройству ls -ln FILE | { read _ _ _ _ size _ && echo "$size"; }не нужен форк для второго этапа конвейера, поскольку он использует только встроенные модули, но Bash 4.2.37 на Linux форкует дважды ( execveхотя все еще только один ).
Palec 04

read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"работает с одиночным fork и single exec, но использует временный файл для данной строки. Его можно сделать переносимым, заменив строку здесь на POSX-совместимый документ . Кстати, обратите внимание execна подоболочку. Без этого Bash выполняет одну вилку для подоболочки, а другую - для выполняющейся внутри команды. Так обстоит дело с кодом, который вы предоставили в этом ответе. слишком.
Palec 04

1
При -lналичии -n. Цитирование POSIX lsсправочной страницы : -n: Включите -l(еП) вариант, но при записи владельца файла или группы, запись числового UID файла или GID , а не имя пользователя или группы, соответственно. Отключить -C, -mи -xварианты.
Palec 04

8

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

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

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

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

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


1
Или поместите это в сценарий оболочки: ls -Lon "$ 1" | awk '{print $ 4}'
Лучано

1
@Luciano Я думаю, вы полностью упустили смысл не разветвлять и не выполнять задачу в bash, а не использовать bash для связывания множества команд unix неэффективным образом.
Orwellophile 08

8

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

stat -f %z <file name> 

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


Однако в Solaris нет никакой statутилиты.
Palec 04

6

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

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

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


5

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

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

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


FYI maxdepth не требуется. Его можно было переписать как size=$(test -f filename && find filename -printf '%s').
Palec

@Palec: -maxdepthпредназначен для предотвращения рекурсии find(поскольку statOP, который необходимо заменить, не является). В вашей findкоманде отсутствует символ, -nameи testкоманда не нужна.
Приостановлено до дальнейшего уведомления.

@DennisWilliamson findрекурсивно ищет в своих параметрах файлы, соответствующие заданным критериям. Если параметры не каталоги, рекурсия… довольно проста. Поэтому я сначала проверяю, что filenameэто действительно существующий обычный файл, а затем распечатываю его размер, используя findто, что рекурсивно некуда.
Palec

1
find . -maxdepth 1 -type f -name filename -printf '%s'работает только в том случае, если файл находится в текущем каталоге, и он все еще может проверять каждый файл в каталоге, что может быть медленным. Лучше использовать (даже короче!) find filename -maxdepth 1 -type f -printf '%s'.
Palec 04

3

Вы можете использовать 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~

2

Ваш первый пример Perl не кажется мне необоснованным.

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

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


Что ж, я сам много пишу на Perl, но иногда инструмент выбирается за меня, а не мной :)

-3

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


-3

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

например, командный файл: вы хотите / dev / null

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


-4

на linux вы можете использовать du -h $FILE, это работает и на solaris?


1
На самом деле, единицы можно преобразовать, но это показывает использование диска, а не размер данных файла («видимый размер»).
Palec 04

-7

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


1
Это показывает использование диска вместо размера данных файла («видимый размер»).
Palec 04
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.