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

Вопрос или проблема

Как я могу получить размер файла в 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); также в BusyBox stat с 2005 года и Toybox stat с 2013 года (обе копируя интерфейс GNU stat):

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

Популярные способы получения размера файла

  1. Использование команды stat

    На системах GNU вы можете использовать команду stat, которая предоставляет информацию о файле, включая его размер. Команда может принимать различные форматные параметры. Для того чтобы получить размер файла в байтах, используйте:

    FILESIZE=$(stat -c%s "/путь/к/вашему/файлу")

    Либо, если вам нужна переносимость с BSD или macOS:

    FILESIZE=$(stat -f %z "/путь/к/вашему/файлу")

    В обоих случаях FILESIZE будет содержать размер файла в байтах.

  2. Использование команды du

    Если ваша основная цель — получение размера занимаемого на диске пространства, вы можете использовать du, что также является надежным и переносимым решением:

    FILESIZE=$(du -b "/путь/к/вашему/файлу" | cut -f1)

    Обратите внимание, что du показывает размер файла в байтах при использовании опции -b.

  3. Использование команды 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.

Не забудьте протестировать ваш код на различных системах, чтобы убедиться в его надежности.

Оцените материал
Добавить комментарий

Капча загружается...