Вопрос или проблема
Как я могу получить размер файла в bash-скрипте?
Как мне присвоить это значение переменной bash, чтобы использовать его позже?
Ваш лучший вариант, если вы на системе GNU:
stat --printf="%s" file.any
Из man stat:
%s общий размер в байтах
В bash-скрипте :
#!/bin/bash
FILENAME=/home/heiko/dummy/packages.txt
FILESIZE=$(stat -c%s "$FILENAME")
echo "Размер $FILENAME = $FILESIZE байт."
ЗАМЕТКА: см. @chbrown’s answer о том, как использовать stat
на системах BSD или macOS.
file_size_kb=`du -k "$filename" | cut -f1`
Проблема с использованием stat
в том, что это расширение GNU (Linux). du -k
и cut -f1
определены POSIX и, следовательно, портируемы на любую Unix-систему.
Solaris, например, поставляется с bash, но не с stat
. Так что это не совсем гипотетично.
ls
имеет аналогичную проблему в том, что точный формат вывода не задан, поэтому парсить его вывод не получится портативно. du -h
также является расширением GNU.
Придерживайтесь портативных конструкций, когда это возможно, и вы облегчите кому-то жизнь в будущем. Возможно, и свою собственную.
Вы также можете использовать команду “подсчет слов” (wc
):
wc -c "$filename" | awk '{print $1}'
Проблема с wc
заключается в том, что она добавит имя файла и сделает вывод с отступом. Например:
$ wc -c somefile.txt
1160 somefile.txt
Если вы хотите избежать связывания полного интерпретируемого языка или потокового редактора только для получения размера файла, просто перенаправьте ввод из файла так, чтобы wc
никогда не видел имя файла:
wc -c < "$filename"
Эта последняя форма может быть использована с подстановкой команд, чтобы легко захватить значение, которое вы искали, как переменную оболочки, как упоминал Gilles ниже.
size="$(wc -c <"$filename")"
Версия stat
в BSD (macOS) имеет другой флаг аргумента формата и разные спецификаторы полей. Из man stat(1)
:
-f format
: Отображает информацию, используя указанный формат. См. раздел FORMATS для описания допустимых форматов.- … раздел FORMATS …
z
: Размер файла в байтах.
Итак, все вместе:
stat -f%z myfile1.txt
ЗАМЕТКА: см. @b01’s answer о том, как использовать команду stat
на системах GNU/Linux. 🙂
Зависит от того, что вы имеете в виду под размером.
size=$(wc -c < "$file")
это даст вам количество байт, которые могут быть прочитаны из файла. Другими словами, это размер содержимого файла. Однако это будет читать содержимое файла (если файл является обычным файлом или символьной ссылкой на обычный файл в большинстве реализаций wc
в качестве оптимизации). Это может иметь побочные эффекты. Например, для именованного канала то, что было прочитано, больше нельзя прочитать, а для таких вещей, как /dev/zero
или /dev/random
, которые имеют бесконечный размер, это займет некоторое время. Это также означает, что вам нужны права на чтение к файлу, и время последнего доступа к файлу может быть обновлено.
Это стандартно и портативно, однако имейте в виду, что некоторые реализации 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, либо размер в байтах подлежащего хранилища.
Вам не нужны права на чтение к файлу, чтобы получить эту информацию, только права на поиск в каталоге, к которому он подключен.
Хронологически¹ порядок таков:
-
IRIX
stat
(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 файла stat +size -- $file # после разрешения символьной ссылки
или чтобы сохранить в переменную:
stat -L -A size +size -- $file
Очевидно, что это наиболее эффективно в этой оболочке.
-
GNU
stat
(2001); также в BusyBoxstat
с 2005 года и Toyboxstat
с 2013 года (обе копируя интерфейс GNUstat
):stat -c %s -- "$file" # st_size файла stat -Lc %s -- "$file" # после разрешения символьной ссылки
(заметьте, что значение
-L
изменено по сравнению с IRIX илиzsh
stat
). -
BSDs
stat
(2002):stat -f %z -- "$file" # st_size файла stat -Lf %z -- "$file" # после разрешения символьной ссылки
Или вы можете использовать функцию 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
, то же самое можно было достичь с помощью команды GNU find
с предикатом -printf
(уже в 1991 году):
find -- "$file" -prune -printf '%s\n' # st_size файла
find -L -- "$file" -prune -printf '%s\n' # после разрешения символьной ссылки
Однако одна проблема заключается в том, что это не работает, если $file
начинается с -
или является предикатом find
(например, !
, (
…).
С версии 4.9 это можно обойти, передав путь файла через стандартный ввод, а не как аргумент, с помощью:
printf '%s\0' "$file" |
find -files0-from - -prune -printf '%s\n'
Стандартная команда для получения информации stat()
/lstat()
— это ls
.
По стандарту POSIX (и в C
, также известном как POSIX
, локале, которая единственная, в которой формат вывода ls -ln
задан POSIX²), вы можете сделать:
LC_ALL=C ls -dln -- "$file" | awk '{print $5; exit}'
(-n
необходимо, чтобы подразумевать -l
, так что последний не должен быть необходим, но вы обнаружите, что в некоторых BSD это требуется).
и добавьте -L
для того же после разрешения символьной ссылки. Это не работает для файлов устройств, где поле размер (5e) заменено на информацию, определяемую реализацией, связанную с устройством (обычно номера мейджор и майнор устройства, иногда разбитые на 2 поля).
Для блочных устройств системы, в которых 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 байт (1024 на HP/UX), которое используется для хранения данных файла (и иногда некоторой его метаданных, таких как расширенные атрибуты на файловых системах ext4 в Linux). Это не включает сам inode или записи в каталогах, к которым связан файл.
Размер и использование диска не обязательно сильно связаны, так как сжатие, разреженность (иногда некоторые метаданные), дополнительная инфраструктура, такая как косвенные блоки в некоторых файловых системах, оказывают влияние на последнее.
Это типично для того, что 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"
¹ Строго говоря, ранние версии UNIX в 70-х, с v1 до v4 имели команду stat
. Она просто выводила информацию из inode и не принимала опции. Она, похоже, исчезла в v5 (1974), предположительно потому, что была избыточной по сравнению с ls -l
.
² За исключением поля информация об устройстве, которое оставлено определяемым реализацией.
Этот скрипт объединяет множество способов для вычисления размера файла:
(
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
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
ls -l filename
даст вам много информации о файле, включая его размер, разрешения и владельца.
Размер файла в пятом столбце и отображается в байтах. В примере ниже размер файла чуть меньше 2KB:
-rw-r--r-- 1 user owner 1985 2011-07-12 16:48 index.php
Редактировать: Это, по-видимому, не так надежно, как команда stat
.
du filename
скажет вам использование диска в байтах.
Мне больше нравится du -h filename
, который дает вам размер в удобном для чтения формате.
Создайте небольшие утилитарные функции в своих оболочках скриптов, которые вы можете делегировать.
Пример
#! /bin/sh -
# vim: set ft=sh
# утилита размера, которая работает на системах GNU и BSD
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) байт (по сравнению с $(size "$f") байт)"
done
На основе информации из ответа @Stéphane Chazelas.
Я нашел однострочный код AWK, и в нем была ошибка, но я ее исправил. Я также добавил петабайты после терабайтов.
FILE_SIZE=234234 # РАЗМЕР ФАЙЛА В БАЙТАХ
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] }')
Учитывая, что stat не доступен на каждой системе, вы почти всегда можете использовать решение AWK. Например, Raspberry Pi не имеет stat, но у него есть awk.
Самый быстрый и простой (по моему мнению) метод:
bash_var=$(stat -c %s /path/to/filename)
stat
работает по-разному на различных системах Unix, и wc
может повторно прочитать весь файл, что неэффективно (иногда ужасно). Размер известен файловой системе, и ls
может получить его для вас:
read -a listing <<< $(ls -ln -- "$file")
echo Размер файла $file ${listing[4]}
Мне нравится вариант с 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
Ответ или решение
Как получить размер файла в Bash скрипте
Вопрос о том, как получить размер файла в Bash, может показаться простым, но есть несколько способов сделать это в зависимости от системной среды и требования к совместимости. В этом ответе мы рассмотрим несколько подходов к определению размера файла, а также то, как назначить этот размер переменной для использования в будущем.
Популярные способы получения размера файла
-
Использование команды
stat
На системах GNU вы можете использовать команду
stat
, которая предоставляет информацию о файле, включая его размер. Команда может принимать различные форматные параметры. Для того чтобы получить размер файла в байтах, используйте:FILESIZE=$(stat -c%s "/путь/к/вашему/файлу")
Либо, если вам нужна переносимость с BSD или macOS:
FILESIZE=$(stat -f %z "/путь/к/вашему/файлу")
В обоих случаях
FILESIZE
будет содержать размер файла в байтах. -
Использование команды
du
Если ваша основная цель — получение размера занимаемого на диске пространства, вы можете использовать
du
, что также является надежным и переносимым решением:FILESIZE=$(du -b "/путь/к/вашему/файлу" | cut -f1)
Обратите внимание, что
du
показывает размер файла в байтах при использовании опции-b
. -
Использование команды
wc
Вы также можете использовать команду
wc
, чтобы узнать размер файла в байтах. Однако здесь нужно быть осторожным, так какwc
может считывать содержимое файла, что может быть нежелательно для больших файлов или специальных файлов, таких как каналы:FILESIZE=$(wc -c < "/путь/к/вашему/файлу")
В этом случае мы перенаправляем ввод из файла с помощью
<
, чтобы избежать включения имени файла в вывод.
Пример Bash-скрипта
Вот пример простого Bash-скрипта, который использует один из вышеуказанных методов для получения размера файла и выводит результат:
#!/bin/bash
# Курс путь к файлу
FILENAME="/путь/к/вашему/файлу"
# Получение размера файла
FILESIZE=$(stat -c%s "$FILENAME") # Для GNU
# FILESIZE=$(stat -f %z "$FILENAME") # Для BSD или macOS
# Вывод результата
echo "Размер файла $FILENAME составляет $FILESIZE байтов."
Заключение
В зависимости от операционной системы и требований к совместимости, вы можете выбрать наиболее подходящий метод для определения размера файла. Использование stat
является наиболее простым и быстрым способом, если он доступен в вашей среде. Если же вы хотите гарантировать совместимость с различными Unix-подобными системами, лучше придерживаться подходов, основанных на du
и wc
.
Не забудьте протестировать ваш код на различных системах, чтобы убедиться в его надежности.