Портативний спосіб отримати розмір файлу (в байтах) в оболонці?


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 просто для отримання розміру файлу? Або працює дві команди, щоб зробити те саме?


1
добре bash-скрипт - це програмне забезпечення, і якщо ви можете помістити це в систему, ви можете встановити програмне забезпечення.
просто хтось

4
Технічно - правда. Я мав на увазі, що я не маю привілеїв root і не можу встановлювати нові пакети. Звичайно, можлива установка в домашньому режимі. Але не дуже, коли мені доводиться робити портативний скрипт, і встановлення на "X" машини нові додаткові пакети стають складними.

Відповіді:


207

wc -c < filename(скорочується кількість слів, -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, тому що в деяких системах він буде мати пробіл перед результатом, який, можливо, знадобиться зняти, перш ніж ви зможете порівняти.
Харавік

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 це варіант для них.
йо '22

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

3
"stat -c" не є портативним / не приймає ті ж аргументи на MacOS, що і в Linux. "wc -c" буде дуже повільним для великих файлів.
Орвелофіл

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

2
POSIX du -bв duобґрунтуванні зазначає зовсім інший контекст .
Палець

Для цього використовується лише lstatвиклик, тому його ефективність не залежить від розміру файлу. Коротше stat -c '%s', але менш інтуїтивно зрозуміло і працює по-іншому для папок (друкує розмір кожного файлу всередині).
Палець

FreeBSDdu може наблизитись до використання du -A -B1, але він все ще друкує результат у кількох блоках 1024B. Не вдалося змусити її друкувати кількість байтів. Навіть налаштування BLOCKSIZE=1в навколишньому середовищі не допомагає, оскільки тоді використовується блок 512B.
Палець

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).
Палець

read _ _ _ _ size _ <<<"$(exec ls -ln /usr/bin/wc)" && echo "$size"працює з одиночною форкою та single exec, але для цього ряду використовується тимчасовий файл. Його можна зробити портативним, замінивши рядок тут на POS-сумісний тут документ . BTW відзначте додаткове місце execв передпласті. Без цього Bash виконує один форк для нижньої частини і інший для команди, що працює всередині. Це справа в коді, який ви вказали у цій відповіді. теж.
Палець

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

8

Найшвидше рішення крос-платформи (використовує лише один 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 для обведення багато команд unix неефективно.
Орвелофіл

8

BSD мають statрізні варіанти від основної програми GNU, але схожі можливості.

stat -f %z <file name> 

Це працює на macOS (тестовано 10.12), FreeBSD , NetBSD та OpenBSD .


statХоча Solaris взагалі не має корисності.
Палець

6

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

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

Це розбиває висновок ln -dnвідповідно до поточних IFSпараметрів змінної середовища, призначає його позиційним аргументам і повторює п'ятий. У -dГарантує каталоги обробляються належним чином і -nгарантує , що імена користувачів і груп не повинні бути вирішені, в відміну від -l. Також імена користувачів та груп, що містять пробіл, теоретично можуть порушити очікувану структуру рядків; їх зазвичай забороняють, але ця можливість все-таки змушує програміста зупинитися і подумати.


5

Якщо ви використовуєте findфайлові утиліти GNU:

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: Це -maxdepthпризначене для запобігання findрекурсивності (оскільки те, statщо ОП потрібно замінити, не є). У вашій findкоманді відсутнє a, -nameі testкоманда не потрібна.
Призупинено до подальшого повідомлення.

@DennisWilliamson findрекурсивно шукає його параметри для файлів, що відповідають заданим критеріям. Якщо параметри не є каталогами, рекурсія… досить проста. Тому я спершу тестую, що filenameце дійсно звичайний файл, а потім надруковую його розмір, використовуючи findщо нікуди повторюватися.
Палець

1
find . -maxdepth 1 -type f -name filename -printf '%s'працює лише в тому випадку, якщо файл знаходиться в поточному каталозі, і він все ще може вивчити кожен файл у каталозі, що може бути повільним. Краще використовувати (ще коротше!) find filename -maxdepth 1 -type f -printf '%s'.
Палець

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

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


-3

У Solaris, який я використав, є хитрість, якщо ви запитуєте розмір більше одного файлу, він повертає лише загальний розмір без імен - тому додайте порожній файл на зразок / dev / null як другий файл:

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

Я не можу запам'ятати, який командний розмір працює для ls / wc / і т. Д. - на жаль, у мене немає вікна solaris, щоб перевірити його.


-4

на Linux ви можете використовувати du -h $FILE, чи працює це і на solaris?


1
Насправді, одиниці можуть бути перетворені, але це показує використання диска замість розміру файлових даних ("видимий розмір").
Палець

-7

Ви пробували du ​​-ks | awk '{print $ 1 * 1024}'. Це може просто працювати.


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