Як я можу отримати розмір файлу в bash-скрипті?


246

Як я можу отримати розмір файлу в bash-скрипті?

Як призначити цю змінну bash, щоб я міг її використовувати пізніше?



1
Пара це з pvі catдля команди копіювання , яка показує прогрес і ETA :)
SUDO

stat -c% s file.name
neverMind9

Відповіді:


242

Ваша найкраща ставка, якщо в системі GNU:

stat --printf="%s" file.any

Від людини :

% s загальний розмір, у байтах

У базовому сценарії:

#!/bin/bash
FILENAME=/home/heiko/dummy/packages.txt
FILESIZE=$(stat -c%s "$FILENAME")
echo "Size of $FILENAME = $FILESIZE bytes."

ПРИМІТКА: див. Відповідь @ chbrown про те, як використовувати stat у терміналі на Mac OS X.


7
@ haunted85 stat- це найпростіший спосіб, припускаючи, що ви використовуєте Linux або Cygwin ( statне стандартно). wc -cза пропозицією Ейгена, це портативно.
Жиль

2
stat: illegal option -- c
Юліан Онофрей

stat --printf="%s" file.txtнічого не виводить на Debian Jessie ...
woohoo

5
На MacOS це працює:stat -f%z myfile.tar
ccpizza

2
@woohoo Ваш запит перезаписує результат. man statговорить, що --printf пропускає останній рядок. Використовуйте --formatабо, -cщоб побачити вихід. Отримати більше розуміння шляхом порівняння stat --printf="%s" file.any | xxd -зstat -c "%s" file.any | xxd -
онуком

92
file_size_kb=`du -k "$filename" | cut -f1`

Проблема з використанням statполягає в тому, що це розширення GNU (Linux). du -kі cut -f1вони визначені POSIX і тому переносяться до будь-якої системи Unix.

Наприклад, "Solaris" кораблі з башем, але не з stat. Тож це не зовсім гіпотетично.

lsє аналогічна проблема в тому, що точний формат виводу не вказаний, тому аналіз його виведення неможливо зробити портативно. du -hтакож є розширенням GNU.

Дотримуйтесь портативних конструкцій там, де це можливо, і ви полегшите комусь життя в майбутньому. Можливо, своє.


48
duне дає розміру файлу, він вказує на те, скільки місця використовується файл, який тонко відрізняється (зазвичай розмір, про який повідомляє, du- це розмір файлу, округлений до найближчої кількості блоків, де блок зазвичай 512B або 1kB або 4kB).
Жиль

7
@Gilles, розріджені файли (тобто, з отворами в них) повідомляють менше довжини.
фонбранд

5
Це, з --bytesабо -bзамість цього -k, має бути прийнятою відповіддю.
Амедей Ван Гассе

1
Варіант -h("людський")du дасть найбільш прийнятну відповідь для загальних випадків: file_size=`du -h "$filename" | cut -f1оскільки він відображатиме K (кілобайти), M (мегабайти) або G (гігабайти), як це доречно.
fralau

1
@fralau: ОП хоче "присвоїти це баш-змінній, щоб вони могли використовувати її згодом", тому набагато більше шансів, що вони хочуть фактичне числове значення, а не наближене до людини наближення. Також -hє розширенням GNU; це не стандартно
Немо

73

Ви також можете скористатися командою "count count" ( wc):

wc -c "$filename" | awk '{print $1}'

Проблема wcполягає в тому, що він додасть ім'я файлу та відступ результату. Наприклад:

$ wc -c somefile.txt
    1160 somefile.txt

Якщо ви хочете уникнути пов'язування повного інтерпретованого мови або редактора потоків просто для отримання підрахунку розміру файлу, просто перенаправляйте введення з файлу, щоб wcніколи не бачилося ім'я файлу:

wc -c < "$filename"

Останню форму можна використовувати з підстановкою команд, щоб легко схопити значення, яке ви шукали як змінну оболонки, про що згадував Гілз нижче.

size="$(wc -c <"$filename")"

30
wc -c <"$FILENAME"таким чином, надає розмір без жодної іншої суглоба size=$(wc -c <"$FILENAME").
Жиль

6
Ще один момент: я щойно тестував це і, wc -c < fileздається, дуже швидко, принаймні на OS X. Я здогадуюсь, що у wc є мізки, щоб спробувати встановити файл, якщо вказано лише -c.
Едвард Фолк

4
@EdwardFalk: GNU wc -cвикористовує fstat, але потім прагне до другого останнього блоку файлу і зчитує останні оновлені st_blksizeбайти. Мабуть, це тому, що файли в Linux /procта, /sysнаприклад, мають розмір stat, який є лише приблизним , і він wcхоче повідомити фактичний розмір, а не розмір, про який повідомляється статистика. Я думаю, було б дивно wc -cповідомляти про інший розмір, ніж це wc, але це не ідея читати дані з файлу, якщо це звичайний файл диска, і це не в пам'яті. Або ще гірше, близькоскладне зберігання стрічки ...
Пітер Кордес,

1
Здається, як і printfраніше бачить відступ, наприклад printf "Size: $size"-> size: <4 spaces> 54339. З іншого боку echoігнорує пробіл. Будь-який спосіб зробити це послідовним?
Євген Кулабухов

2
@keithpjolley: Зателефонувавши fstat. Спробуйте запустити, strace wc -c </etc/passwdі ви зможете побачити, що це робить.
Немо

48

BSD (Mac OS X) statмає різний прапор аргументу формату та різні специфікатори поля. Від man stat(1):

  • -f format: Відображення інформації у вказаному форматі. Дивіться розділ ФОРМАТИ для опису дійсних форматів.
  • ... розділ ФОРМАТИ ...
  • z: Розмір файлу в байтах.

Тож усі разом зараз:

stat -f%z myfile1.txt

28

Залежить від того, що ви маєте на увазі під розміром .

size=$(wc -c < "$file")

дасть вам кількість байтів, які можна прочитати з файлу. IOW, це розмір вмісту файлу. Однак він буде читати вміст файлу (за винятком випадків, коли файл є звичайним файлом або символьним посиланням на звичайний файл у більшості wcреалізацій як оптимізація). Це може мати побічні ефекти. Наприклад, для названої труби прочитане вже не можна прочитати заново, а для речей, як-от /dev/zeroабо /dev/randomякі мають нескінченний розмір, знадобиться певний час. Це також означає, що вам потрібен readдозвіл на файл, і остання часова мітка доступу до цього файлу може бути оновлена.

Це стандартно і портативно, однак зауважте, що деякі wcреалізації можуть включати провідні пробіли у цьому висновку. Одним із способів позбутися від них є використання:

size=$(($(wc -c < "$file")))

або щоб уникнути помилки щодо порожнього арифметичного вираження в dashабо yashколи wcне видає вихід (наприклад, коли файл неможливо відкрити):

size=$(($(wc -c < "$file") +0))

ksh93має wcвбудований (за умови включення, ви також можете викликати його як command /opt/ast/bin/wc), що робить його найбільш ефективним для звичайних файлів у цій оболонці.

У різних системах є команда, яка називається statінтерфейсом до stat()або lstat()системних викликів.

Інформація про звіт, знайдена в inode. Однією з цих відомостей є st_sizeатрибут. Для звичайних файлів - це розмір вмісту (скільки даних можна прочитати з нього за відсутності помилок (саме це wc -cвикористовує більшість реалізацій при їх оптимізації)). Для символьних посилань, це розмір у байтах цільового шляху. Для названих труб, залежно від системи, це або 0, або кількість байтів, що знаходяться в буфері труби. Те саме для блокових пристроїв, де залежно від системи ви отримуєте 0 або розмір у байтах базового сховища.

Щоб отримати цю інформацію, вам не потрібно дозволу на читання файлу, лише дозвіл на пошук до каталогу, до якого він пов'язаний.

За хронологічним порядком існує:

  • IRIXstat (90-ті):

    stat -qLs -- "$file"

    повертає st_sizeатрибут $file( lstat()) або:

    stat -s -- "$file"

    те саме, за винятком випадків, коли $fileє символьним st_sizeпосиланням;

  • zsh statвбудований (зараз також відомий як zstat) zsh/statмодуль (завантажений zmodload zsh/stat) (1997):

    stat -L +size -- $file # st_size of file
    stat +size -- $file    # after symlink resolution
    

    або зберігати в змінній:

    stat -L -A size +size -- $file

    очевидно, що це найефективніше в цій оболонці.

  • ГНУstat (2001); також у BusyBox statз 2005 року (скопійовано з GNU stat):

    stat -c %s -- "$file"  # st_size of file
    stat -Lc %s -- "$file" # after symlink resolution
    

    (зауважте, значення значення -Lзворотне порівняно з IRIX або zsh stat.

  • BSDstat (2002):

    stat -f %z -- "$file"  # st_size of file
    stat -Lf %z -- "$file" # after symlink resolution
    

Або ви можете використовувати stat()/ lstat()функцію деяких сценаріїв, таких як perl:

perl -le 'print((lstat shift)[7])' -- "$file"

AIX також має istatкоманду, яка скидає всю інформацію stat()(не lstat(), тому не буде працювати над символьними посиланнями) і яку ви могли б після обробки, наприклад:

LC_ALL=C istat "$file" | awk 'NR == 4 {print $5}'

(дякую @JeffSchaller за допомогу в з'ясуванні деталей ).

В tcsh:

@ size = -Z $file:q

(розмір після роздільної здатності символьної посилання)

Задовго до того, як GNU представила свою statкоманду, те саме можна було досягти і з findкомандою GNU з її -printfпредикатом (вже в 1991 році):

find -- "$file" -prune -printf '%s\n'    # st_size of file
find -L -- "$file" -prune -printf '%s\n' # after symlink resolution

Одне питання, однак, це те, що він не працює, якщо $fileпочинається з предиката -або є find(наприклад !, (...).

Стандартною командою для отримання stat()/ lstat()інформації є ls.

POSIXly, ви можете:

LC_ALL=C ls -dn -- "$file" | awk '{print $5; exit}'

і додайте -Lдля того ж після роздільної здатності симпосилання. Це не працює для файлів пристрою, хоча там, де 5- е поле є основним номером пристрою замість розміру.

Для блокових пристроїв системи, де stat()повертається 0 для st_size, зазвичай мають інші API, щоб повідомити про розмір блокового пристрою. Наприклад, Linux має BLKGETSIZE64 ioctl(), і більшість дистрибутивів Linux зараз постачається з blockdevкомандою, яка може ним скористатися:

blockdev --getsize64 -- "$device_file"

Однак для цього вам потрібен дозвіл на читання файлу пристрою. Зазвичай розмір можна отримати іншими способами. Наприклад (все ще в Linux):

lsblk -bdno size -- "$device_file"

Має працювати, крім порожніх пристроїв.

Підхід, який працює для всіх файлів, що шукаються (тому включає звичайні файли, більшість блокових пристроїв та деякі пристрої символів), полягає в тому, щоб відкрити файл і домагатися до кінця:

  • З zsh(після завантаження zsh/systemмодуля):

    {sysseek -w end 0 && size=$((systell(0)))} < $file
  • З ksh93:

    < "$file" <#((size=EOF))

    або

    { size=$(<#((EOF))); } < "$file"
  • з perl:

    perl -le 'seek STDIN, 0, 2 or die "seek: $!"; print tell STDIN' < "$file"

Для названих труб, ми бачили , що деякі системи (AIX, Solaris, HP / UX , по крайней мере) зробити обсяг даних в буфері труб , наявних в stat()«з st_size. Деякі (наприклад, Linux або FreeBSD) цього не роблять.

Принаймні, у Linux ви можете використовувати FIONREAD ioctl()після відкриття труби (у режимі читання + запису, щоб уникнути її вивішування):

fuser -s -- "$fifo_file" && 
  perl -le 'require "sys/ioctl.ph";
            ioctl(STDIN, &FIONREAD, $n) or die$!;
            print unpack "L", $n' <> "$fifo_file"

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

Тепер ми розглядали лише розмір первинних даних, пов'язаних з файлами. Це не враховує розмір метаданих та всю підтримуючу інфраструктуру, необхідну для зберігання цього файлу.

Ще один атрибут inode, який повертає, stat()є st_blocks. Це кількість 512 байтових блоків, які використовуються для зберігання даних файлу (а іноді і деяких його метаданих, як розширені атрибути у файлових системах ext4 в Linux). Це не включає сам вклад або записи в каталогах, до яких файл пов'язаний.

Розмір і використання диска не обов'язково пов'язані між собою як стиснення, розрідженість (іноді деякі метадані), додаткова інфраструктура, як непрямі блоки в деяких файлових системах, впливають на останні.

Це зазвичай те, що duвикористовується для повідомлення звіту про використання диска. Більшість команд, перерахованих вище, зможуть отримати вам цю інформацію.

  • POSIXLY_CORRECT=1 ls -sd -- "$file" | awk '{print $1; exit}'
  • POSIXLY_CORRECT=1 du -s -- "$file" (не для каталогів, де це включало б використання дискових файлів всередині).
  • GNU find -- "$file" -printf '%b\n'
  • zstat -L +block -- $file
  • GNU stat -c %b -- "$file"
  • BSD stat -f %b -- "$file"
  • perl -le 'print((lstat shift)[12])' -- "$file"

Очевидно найбільш вичерпна та інформаційна відповідь. спасибі. я можу використовувати це для створення крос-платформних скриптів bash, використовуючи інформацію про статистику BSD та GNU
oligofren

1
Забавний факт: GNU coreutils wc -cвикористовує fstat, але потім зчитує останні оновлені st_blksizeбайти. Мабуть, це тому, що файли в Linux /procта, /sysнаприклад, мають розміри stat, які є лише приблизними . Це добре для правильності, але погано, якщо кінець файлу знаходиться на диску, а не в пам'яті (особливо, якщо він використовується для багатьох файлів у циклі). І дуже погано, якщо файл буде переміщено до ближнього сховища стрічки або, наприклад, FUSE прозоро-декомпресійної файлової системи.
Пітер Кордес

Не хотів би і цю роботуls -go file | awk '{print $3}'
Стівен Пенні

@StevenPenny це -goбули б SysV, вони не працювали б на BSD (необов'язково (XSI) в POSIX). Вам також знадобиться ls -god file | awk '{print $3; exit}'( -dщоб він працював над каталогами, exitдля посилань з новими рядками в цілі). Проблеми з файлами пристроїв також залишаються.
Стефан Шазелас

1
@ αғsnιη Unix API не робить різниці між текстовими та бінарними файлами. Це всі послідовності байтів. Деякі програми можуть хотіти інтерпретувати ці байти як текст, але, очевидно, не, wc -cщо повідомляє про кількість байтів.
Стефан Шазелас

22

Цей сценарій поєднує в собі багато способів обчислення розміру файлу:

(
  du --apparent-size --block-size=1 "$file" 2>/dev/null ||
  gdu --apparent-size --block-size=1 "$file" 2>/dev/null ||
  find "$file" -printf "%s" 2>/dev/null ||
  gfind "$file" -printf "%s" 2>/dev/null ||
  stat --printf="%s" "$file" 2>/dev/null ||
  stat -f%z "$file" 2>/dev/null ||
  wc -c <"$file" 2>/dev/null
) | awk '{print $1}'

Сценарій працює в багатьох системах Unix, включаючи Linux, BSD, OSX, Solaris, SunOS тощо.

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

Цей сценарій має виробничу версію з більшою довідкою та іншими параметрами тут: https://github.com/SixArm/file-size


9

stat, схоже, робить це за допомогою найменших системних викликів:

$ set debian-live-8.2.0-amd64-xfce-desktop.iso

$ strace stat --format %s $1 | wc
    282    2795   27364

$ strace wc --bytes $1 | wc
    307    3063   29091

$ strace du --bytes $1 | wc
    437    4376   41955

$ strace find $1 -printf %s | wc
    604    6061   64793

8

ls -l filename дасть вам багато інформації про файл, включаючи його розмір, дозволи та власника.

Розмір файлу в п'ятому стовпці і відображається в байтах. У наведеному нижче прикладі розмір файлів трохи менше 2 КБ:

-rw-r--r-- 1 user owner 1985 2011-07-12 16:48 index.php

Правка: Це, мабуть, не так надійно, як statкоманда.


Я думаю, що обидва ls -lі statкоманда дають достовірну інформацію про розмір. Я не знайшов жодного посилання на протилежне. ls -sдасть розмір у кількості блоків.
dabest1

2
@ dabest1 не є надійним в тому сенсі, що в іншому Unix їх вихід може бути різним (а в деяких Unixes - це і є).
Євген Буяк

Так, IIRC, Solaris не відображали назву групи за замовчуванням, що призводило до зменшення стовпців у висновку.
Едвард Фолк

Оскільки розмір є чистим числовим, оточений пробілом, а дата дати є чистим числовим, у визначеному форматі, можна було б використовувати регулярний параметр для обробки користувача + власника як одного поля, незалежно від того, присутня група чи ні. (вправа для читача!)
MikeW

5

du filename підкаже вам про використання диска в байтах.

Я вважаю за краще du -h filename, що надає вам розмір у читаному для людини форматі.


2
що або stat -c "%s";)

1
Цей аромат duвиводить розмір у блоках на 1024 байти, а не просте підрахунок байтів.
Пітер Ліонс

Зауважте, що стандарт duдає вихід у кількості 512-байтних одиниць. GNU duзамість цього використовує кібібайти, якщо тільки не викликається POSIXLY_CORRECTу своєму оточенні.
Стефан Шазелас

1
Для файлів типу каталогів , що дає дисковому використанню каталог, але також і всі інші файли всередині (рекурсивно).
Стефан Шазелас

3

Створіть невеликі функції утиліти у своїх скриптах оболонки, до яких можна делегувати.

Приклад

#! /bin/sh -
# vim: set ft=sh

# size utility that works on GNU and BSD systems
size(){
    case $(uname) in
        (Darwin | *BSD*)
            stat -Lf %z -- "$1";;
        (*) stat -c %s -- "$1"
    esac
}

for f do
    printf '%s\n' "$f : $(gzip < "$f" | wc -c) bytes (versus $(size "$f") bytes)"
done

На основі інформації з відповіді @ Stéphane Chazelas.


Дивіться також, gzip -v < file > /dev/nullщоб перевірити стисливість файлу.
Стефан Шазелас

@ StéphaneChazelas не впевнений, чи я думаю, що це було поліпшенням. ці випадки випадків можуть легко відключити ноб. Я, звичайно, ніколи не пам’ятаю, як правильно їх виправити :-), чи справді викладені справи є більш портативними, оскільки ви це зробили? Я бачу сенс, коли є більше двох випадків, але в іншому випадку ... +
oligofren

1
Я припускаю, що це теж питання смаку, але ось типовий випадок, коли ви хочете використовувати caseвислів. case- це конструкція Bourne / POSIX для узгодження шаблонів. [[...]]є лише ksh / bash / zsh (з варіаціями).
Стефан Шазелас

2

Я знайшов вкладиш AWK 1, у ньому була помилка, але я її виправив. Я також додав у PetaBytes після TeraBytes.

FILE_SIZE=234234 # FILESIZE IN BYTES
FILE_SIZE=$(echo "${FILE_SIZE}" | awk '{ split( "B KB MB GB TB PB" , v ); s=1; while( $1>1024 ){ $1/=1024; s++ } printf "%.2f %s", $1, v[s] }')

Зважаючи на те, що статистика є не в кожній окремій системі, ви майже завжди можете використовувати рішення AWK. Приклад; малина Pi не має Мінстату , але у нього є AWK .


1
Повністю НЕ того, що попросив ОП, але приємного маленького твору.
Циганський заклинатель

0

Ще один спосіб відповідає POSIX буде використовувати awkз length()функцією , яка повертає довжину в символах на кожному рядку вхідного файлу, за винятком символу нового рядка. Так роблячи

awk '{ sum+=length } END { print sum+NR }' file

ми гарантуємо NR, що додано до цього sum, що призводить до загальної кількості символів та загальної кількості нових рядків, що зустрічаються у файлі. length()Функція awkприймає яка аргумент за допомогою по замовчуванням , length($0)який є для поточної всій лінії.


Не якщо останній рядок не закінчується на новому рядку: printf 'a\nb' | awk '{ sum+=length } END { print sum+NR }'слід надрукувати 3, але друкує 4.
Ісаак

-1

Мені подобається варіант wc сам. У поєднанні з "bc" ви можете отримати десяткову плату в будь-якій кількості місць, де вам заманеться.

Я шукав, щоб покращити сценарій, який у мене з'явився стовпець "розмір файлу" команди "ls -alh". Я не хотів просто цілих розмірів файлів, і два десяткові дроби, здавалося, підходять, тому, прочитавши цю дискусію, я придумав код нижче.

Я пропоную розбити рядок на крапку з комою, якщо ви включите це в сценарій.

file=$1; string=$(wc -c $file); bite=${string% *}; okay=$(echo "scale=2; $bite/1024" | bc);friend=$(echo -e "$file $okay" "kb"); echo -e "$friend"

Мій сценарій називається gpfl , щоб "отримати довжину файлу зображення". Я використовую його після того, як роблю змінити файл у Imagemagick, перед відкриттям або повторним завантаженням зображення в переглядач jpeg GUI.

Я не знаю, як це оцінюється як "відповідь", оскільки він позичає багато з того, що вже було запропоновано та обговорено. Тож я залишу його там.

BZT


1
Я вважаю за краще використовувати "stat" або "ls". Зазвичай мені не подобається використовувати "wc" для отримання розмірів файлів, оскільки він фізично читає весь файл. Якщо у вас багато файлів або особливо великих файлів, це може зайняти багато часу. Але ваше рішення творче ... + 1.
Кевін Феган

2
Я погоджуюся з поняттям використання "stat" над "wc" для розміру файлів, однак якщо ви використовуєте "wc -c", дані не будуть прочитані; натомість lseek буде використовуватися для визначення кількості байтів у файлі. lingrok.org/xref/coreutils/src/wc.c#228
bbaja42

1
@ bbaja42: зауважте, що GNU Coreutils wcчитає останній блок файлу, на випадок, якщо це stat.st_sizeбуло лише наближення (як для Linux /procта /sysфайлів). Я думаю, що вони вирішили не ускладнювати головний коментар, коли додали, що логіка пара рядків вниз: lingrok.org/xref/coreutils/src/wc.c#246
Пітер Кордес

-1

Найшвидший і найпростіший (ІМО) метод:

bash_var=$(stat -c %s /path/to/filename)

2
Потім затвердіть одну або декілька існуючих відповідей, в яких згадується статистика; не потрібно повторювати це знову ...
Джефф Шаллер

1
@JeffSchaller Я щойно підтримав відповідь Стефана на ваші вказівки. Я думаю, що це занадто складно для моїх цілей. Ось чому я опублікував цю просту відповідь для однодумців.
WinEunuuchs2Unix

1
Дякую; Просто шостий екземпляр відповіді "stat" не спрощує це запитання, а скоріше змусить нового читача запитати себе: "чим ця відповідь відрізняється від інших?" і призводять до більшої плутанини замість меншої.
Джефф Шаллер

@JeffSchaller я здогадуюсь. Але я міг би скаржитися багато duі wcвідповіді , які повинні мати відмова від відповідальності НІКОЛИ НЕ ЦЕ в реальному житті. Я просто використав свою відповідь у додатку в реальному житті сьогодні ввечері і вважав, що варто поділитися. Я думаю, що всі ми знизуємо свою думку .
WinEunuuchs2Unix
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.