Как я могу получить размер файла в bash-скрипте?


247

Как я могу получить размер файла в bash-скрипте?

Как мне присвоить это переменной bash, чтобы я мог использовать ее позже?



1
Соедините это с pvи catдля команды копирования, которая показывает прогресс и ETA :)
sudo

stat -c% s file.name
neverMind9

Ответы:


243

Ваш лучший выбор, если в системе GNU:

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

От человека стат :

% s общий размер, в байтах

В скрипте bash:

#!/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, Джесси ...
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.

Солярис, например, поставляется с Bash, но не с stat. Так что это не совсем гипотетически.

lsимеет аналогичную проблему в том, что точный формат вывода не указан, поэтому анализ его вывода не может быть выполнен переносимым. du -hтакже расширение GNU.

Придерживайтесь переносных конструкций, где это возможно, и в будущем вы сделаете чью-то жизнь проще. Может быть, ваш собственный.


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

7
@ Жиль, разреженные файлы (то есть файлы с дырами в них) сообщают о длине меньше.
vonbrand

5
Это, с --bytesили -bвместо -k, должен быть принятым ответом.
Амеди Ван Гасс

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

1
@fralau: OP хочет «назначить это переменной bash, чтобы они могли использовать ее позже», так что гораздо более вероятно, что они хотят получить фактическое числовое значение, а не понятное для человека приближение. Кроме того, -hэто расширение GNU; это не стандартно
Nemo

74

Вы также можете использовать команду «подсчет слов» ( 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например, имеют размеры статистики, которые являются только приблизительными , и 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: Отображение информации в указанном формате. Смотрите раздел FORMATS для описания допустимых форматов.
  • ... раздел ФОРМАТЫ ...
  • 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

    очевидно, это самый эффективный в этой оболочке.

  • GNUstat (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.

  • BSDsstat (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()'s 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()is 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например, имеют размеры статистики, которые являются только приблизительными . Это хорошо для корректности, но плохо, если конец файла находится на диске, а не в памяти (особенно, если он используется во многих файлах в цикле). И очень плохо, если файл переносится в ленточное хранилище с близким сроком хранения или, например, в файловую систему с прозрачной декомпрессией FUSE.
Питер Кордес

не будет ли эта работа тожеls -go file | awk '{print $3}'
Стивен Пенни

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

1
@ αғsнιη 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 их вывод может быть другим (а в некоторых Unix это так).
Евгений Бужак

Да, IIRC, Solaris не отображал имя группы по умолчанию, что привело к уменьшению количества столбцов в выводе.
Эдвард Фальк

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

5

du filename скажет вам использование диска в байтах.

Я предпочитаю du -h filename, который дает вам размер в удобочитаемом формате.


2
что или stat -c "%s";)

1
Этот аромат duвыводит размер в блоках по 1024 байта, а не в простое число байтов.
Питер Лайонс

Обратите внимание, что стандарт duдает выход в количестве 512 байт. duВместо этого GNU использует кибибайты, если 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

Основано на информации из ответа Стефана Шазеля.


Смотрите также, 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. Пример; у Raspberry 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 , для «получить длину файла изображения». Я использую его после выполнения mogrify для файла в imagemagick, перед открытием или повторной загрузкой изображения в программе просмотра JPEG с графическим интерфейсом.

Я не знаю, как это оценивается как «ответ», поскольку он многое заимствует из того, что уже было предложено и обсуждено. Так что я оставлю это там.

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.