Вычислить размер каталога с помощью Python?

Прежде чем я заново изобрел этот конкретный круг, есть ли у кого-нибудь хорошая процедура для расчета размера каталога с использованием Python? Было бы очень хорошо, если бы подпрограмма хорошо форматировала размер в Мб/Гб и т.д.

Ответ 1

Это идет по всем подкаталогам; Суммирование размеров файла:

import os

def get_size(start_path = '.'):
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(start_path):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            # skip if it is symbolic link
            if not os.path.islink(fp):
                total_size += os.path.getsize(fp)

    return total_size

print(get_size(), 'bytes')

И еще один интересный способ использовать os.listdir (не включает подкаталоги):

import os
sum(os.path.getsize(f) for f in os.listdir('.') if os.path.isfile(f))

Ссылка:

Обновлено Чтобы использовать os.path.getsize, это понятнее, чем использование метода os.stat(). St_size.

Спасибо ghostdog74 за это!

os.stat - st_size Предоставляет размер в байтах. Может также использоваться для получения размера файла и другой информации, связанной с файлом.

Обновление 2018

Если вы используете Python 3.4 или более раннюю версию, вы можете рассмотреть возможность использования более эффективного метода walk, предоставляемого сторонним пакетом scandir. В Python 3.5 и более поздних версиях этот пакет был включен в стандартную библиотеку, и os.walk получил соответствующее увеличение производительности.

Обновление 2019

В последнее время я использую pathlib все больше и больше, вот решение pathlib:

from pathlib import Path

root_directory = Path('.')
sum(f.stat().st_size for f in root_directory.glob('**/*') if f.is_file() )

Ответ 2

Некоторые из предложенных до сих пор подходов реализуют рекурсию, другие используют оболочку или не будут производить аккуратно отформатированные результаты. Когда ваш код является одноразовым для платформ Linux, вы можете получить форматирование, как обычно, рекурсию в качестве однострочного. За исключением print в последней строке, он будет работать для текущих версий python2 и python3:

du.py
-----
#!/usr/bin/python3
import subprocess

def du(path):
    """disk usage in human readable format (e.g. '2,1GB')"""
    return subprocess.check_output(['du','-sh', path]).split()[0].decode('utf-8')

if __name__ == "__main__":
    print(du('.'))

прост, эффективен и будет работать для файлов и многоуровневых каталогов:

$ chmod 750 du.py
$ ./du.py
2,9M

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

Ответ 3

Вот рекурсивная функция (она рекурсивно суммирует размер всех подпапок и их соответствующих файлов), которая возвращает точно такие же байты, что и при запуске "du-sb". в linux (где "." означает "текущая папка" ):

import os

def getFolderSize(folder):
    total_size = os.path.getsize(folder)
    for item in os.listdir(folder):
        itempath = os.path.join(folder, item)
        if os.path.isfile(itempath):
            total_size += os.path.getsize(itempath)
        elif os.path.isdir(itempath):
            total_size += getFolderSize(itempath)
    return total_size

print "Size: " + str(getFolderSize("."))

Ответ 4

Размер рекурсивной папки Python 3.5 с помощью os.scandir

def folder_size(path='.'):
    total = 0
    for entry in os.scandir(path):
        if entry.is_file():
            total += entry.stat().st_size
        elif entry.is_dir():
            total += folder_size(entry.path)
    return total

Ответ 5

В принятом ответе не учитываются жесткие или непрямые ссылки и будут считать эти файлы дважды. Вы хотите отслеживать, какие индексы вы видели, и не добавлять размер для этих файлов.

import os
def get_size(start_path='.'):
    total_size = 0
    seen = {}
    for dirpath, dirnames, filenames in os.walk(start_path):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            try:
                stat = os.stat(fp)
            except OSError:
                continue

            try:
                seen[stat.st_ino]
            except KeyError:
                seen[stat.st_ino] = True
            else:
                continue

            total_size += stat.st_size

    return total_size

print get_size()

Ответ 6

Ответ monknut хорош, но он не работает на сломанной символической ссылке, поэтому вам также нужно проверить, действительно ли этот путь существует

if os.path.exists(fp):
    total_size += os.stat(fp).st_size

Ответ 7

Ответ Криса хорош, но может быть более идиоматичным, если использовать набор для проверки каталогов, который также избегает использования исключения для потока управления:

def directory_size(path):
    total_size = 0
    seen = set()

    for dirpath, dirnames, filenames in os.walk(path):
        for f in filenames:
            fp = os.path.join(dirpath, f)

            try:
                stat = os.stat(fp)
            except OSError:
                continue

            if stat.st_ino in seen:
                continue

            seen.add(stat.st_ino)

            total_size += stat.st_size

    return total_size  # size in bytes

Ответ 8

рекурсивный однострочный:

def getFolderSize(p):
   from functools import partial
   prepend = partial(os.path.join, p)
   return sum([(os.path.getsize(f) if os.path.isfile(f) else getFolderSize(f)) for f in map(prepend, os.listdir(p))])

Ответ 9

Для второй части вопроса

def human(size):

    B = "B"
    KB = "KB" 
    MB = "MB"
    GB = "GB"
    TB = "TB"
    UNITS = [B, KB, MB, GB, TB]
    HUMANFMT = "%f %s"
    HUMANRADIX = 1024.

    for u in UNITS[:-1]:
        if size < HUMANRADIX : return HUMANFMT % (size, u)
        size /= HUMANRADIX

    return HUMANFMT % (size,  UNITS[-1])

Ответ 10

Вы можете сделать что-то вроде этого:

import commands   
size = commands.getoutput('du -sh /path/').split()[0]

В этом случае я не тестировал результат перед его возвратом, если вы хотите, чтобы вы могли проверить его с помощью команд .getstatusoutput.

Ответ 11

Один слот, который вы говорите... Вот один лайнер:

sum([sum(map(lambda fname: os.path.getsize(os.path.join(directory, fname)), files)) for directory, folders, files in os.walk(path)])

Хотя я бы, вероятно, разделил его и не выполнил никаких проверок.

Чтобы преобразовать в kb см. Многоразовую библиотеку, чтобы получить удобочитаемую версию размера файла? и работать с ней в

Ответ 12

Немного поздно для вечеринки, но в одной строке при условии, что у вас glob2 и humanize. Обратите внимание, что в Python 3 по умолчанию iglob имеет рекурсивный режим. Как изменить код для Python 3 оставлен как тривиальное упражнение для читателя.

>>> import os
>>> from humanize import naturalsize
>>> from glob2 import iglob
>>> naturalsize(sum(os.path.getsize(x) for x in iglob('/var/**'))))
'546.2 MB'

Ответ 13

Следующий script печатает размер каталога для всех подкаталогов для указанного каталога. Он также пытается извлечь выгоду (если возможно) из кеширования вызовов рекурсивных функций. Если аргумент опущен, script будет работать в текущем каталоге. Результат сортируется по размеру каталога от самых больших до самых маленьких. Поэтому вы можете адаптировать его для своих нужд.

PS Я использовал рецепт 578019 для отображения размера каталога в удобном для человека формате (http://code.activestate.com/recipes/578019/)

from __future__ import print_function
import os
import sys
import operator

def null_decorator(ob):
    return ob

if sys.version_info >= (3,2,0):
    import functools
    my_cache_decorator = functools.lru_cache(maxsize=4096)
else:
    my_cache_decorator = null_decorator

start_dir = os.path.normpath(os.path.abspath(sys.argv[1])) if len(sys.argv) > 1 else '.'

@my_cache_decorator
def get_dir_size(start_path = '.'):
    total_size = 0
    if 'scandir' in dir(os):
        # using fast 'os.scandir' method (new in version 3.5)
        for entry in os.scandir(start_path):
            if entry.is_dir(follow_symlinks = False):
                total_size += get_dir_size(entry.path)
            elif entry.is_file(follow_symlinks = False):
                total_size += entry.stat().st_size
    else:
        # using slow, but compatible 'os.listdir' method
        for entry in os.listdir(start_path):
            full_path = os.path.abspath(os.path.join(start_path, entry))
            if os.path.isdir(full_path):
                total_size += get_dir_size(full_path)
            elif os.path.isfile(full_path):
                total_size += os.path.getsize(full_path)
    return total_size

def get_dir_size_walk(start_path = '.'):
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(start_path):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            total_size += os.path.getsize(fp)
    return total_size

def bytes2human(n, format='%(value).0f%(symbol)s', symbols='customary'):
    """
    (c) http://code.activestate.com/recipes/578019/

    Convert n bytes into a human readable string based on format.
    symbols can be either "customary", "customary_ext", "iec" or "iec_ext",
    see: http://goo.gl/kTQMs

      >>> bytes2human(0)
      '0.0 B'
      >>> bytes2human(0.9)
      '0.0 B'
      >>> bytes2human(1)
      '1.0 B'
      >>> bytes2human(1.9)
      '1.0 B'
      >>> bytes2human(1024)
      '1.0 K'
      >>> bytes2human(1048576)
      '1.0 M'
      >>> bytes2human(1099511627776127398123789121)
      '909.5 Y'

      >>> bytes2human(9856, symbols="customary")
      '9.6 K'
      >>> bytes2human(9856, symbols="customary_ext")
      '9.6 kilo'
      >>> bytes2human(9856, symbols="iec")
      '9.6 Ki'
      >>> bytes2human(9856, symbols="iec_ext")
      '9.6 kibi'

      >>> bytes2human(10000, "%(value).1f %(symbol)s/sec")
      '9.8 K/sec'

      >>> # precision can be adjusted by playing with %f operator
      >>> bytes2human(10000, format="%(value).5f %(symbol)s")
      '9.76562 K'
    """
    SYMBOLS = {
        'customary'     : ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'),
        'customary_ext' : ('byte', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa',
                           'zetta', 'iotta'),
        'iec'           : ('Bi', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'),
        'iec_ext'       : ('byte', 'kibi', 'mebi', 'gibi', 'tebi', 'pebi', 'exbi',
                           'zebi', 'yobi'),
    }
    n = int(n)
    if n < 0:
        raise ValueError("n < 0")
    symbols = SYMBOLS[symbols]
    prefix = {}
    for i, s in enumerate(symbols[1:]):
        prefix[s] = 1 << (i+1)*10
    for symbol in reversed(symbols[1:]):
        if n >= prefix[symbol]:
            value = float(n) / prefix[symbol]
            return format % locals()
    return format % dict(symbol=symbols[0], value=n)

############################################################
###
###  main ()
###
############################################################
if __name__ == '__main__':
    dir_tree = {}
    ### version, that uses 'slow' [os.walk method]
    #get_size = get_dir_size_walk
    ### this recursive version can benefit from caching the function calls (functools.lru_cache)
    get_size = get_dir_size

    for root, dirs, files in os.walk(start_dir):
        for d in dirs:
            dir_path = os.path.join(root, d)
            if os.path.isdir(dir_path):
                dir_tree[dir_path] = get_size(dir_path)

    for d, size in sorted(dir_tree.items(), key=operator.itemgetter(1), reverse=True):
        print('%s\t%s' %(bytes2human(size, format='%(value).2f%(symbol)s'), d))

    print('-' * 80)
    if sys.version_info >= (3,2,0):
        print(get_dir_size.cache_info())

Пример вывода:

37.61M  .\subdir_b
2.18M   .\subdir_a
2.17M   .\subdir_a\subdir_a_2
4.41K   .\subdir_a\subdir_a_1
----------------------------------------------------------
CacheInfo(hits=2, misses=4, maxsize=4096, currsize=4)

EDIT: перемещен null_decorator выше, так как пользователь2233949 рекомендуется

Ответ 14

использовать библиотеку sh: модуль du делает это:

pip install sh

import sh
print( sh.du("-s", ".") )
91154728        .

если вы хотите передать asterix, используйте glob, как описано здесь.

чтобы преобразовать значения в считываемые данные человека, используйте humanize:

pip install humanize

import humanize
print( humanize.naturalsize( 91157384 ) )
91.2 MB

Ответ 15

Это удобно:

import os
import stat

size = 0
path_ = ""
def calculate(path=os.environ["SYSTEMROOT"]):
    global size, path_
    size = 0
    path_ = path

    for x, y, z in os.walk(path):
        for i in z:
            size += os.path.getsize(x + os.sep + i)

def cevir(x):
    global path_
    print(path_, x, "Byte")
    print(path_, x/1024, "Kilobyte")
    print(path_, x/1048576, "Megabyte")
    print(path_, x/1073741824, "Gigabyte")

calculate("C:\Users\Jundullah\Desktop")
cevir(size)

Output:
C:\Users\Jundullah\Desktop 87874712211 Byte
C:\Users\Jundullah\Desktop 85815148.64355469 Kilobyte
C:\Users\Jundullah\Desktop 83803.85609722137 Megabyte
C:\Users\Jundullah\Desktop 81.83970321994275 Gigabyte

Ответ 16

Используя pathlib я придумал этот pathlib чтобы получить размер папки:

sum(file.stat().st_size for file in Path(folder).rglob('*'))

И вот что я придумал для красиво отформатированного вывода:

from pathlib import Path


def get_folder_size(folder):
    return ByteSize(sum(file.stat().st_size for file in Path(folder).rglob('*')))


class ByteSize(int):

    _kB = 1024
    _suffixes = 'B', 'kB', 'MB', 'GB', 'PB'

    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        self.bytes = self.B = int(self)
        self.kilobytes = self.kB = self / self._kB**1
        self.megabytes = self.MB = self / self._kB**2
        self.gigabytes = self.GB = self / self._kB**3
        self.petabytes = self.PB = self / self._kB**4
        *suffixes, last = self._suffixes
        suffix = next((
            suffix
            for suffix in suffixes
            if 1 < getattr(self, suffix) < self._kB
        ), last)
        self.readable = suffix, getattr(self, suffix)

        super().__init__()

    def __str__(self):
        return self.__format__('.2f')

    def __repr__(self):
        return '{}({})'.format(self.__class__.__name__, super().__repr__())

    def __format__(self, format_spec):
        suffix, val = self.readable
        return '{val:{fmt}} {suf}'.format(val=val, fmt=format_spec, suf=suffix)

    def __sub__(self, other):
        return self.__class__(super().__sub__(other))

    def __add__(self, other):
        return self.__class__(super().__add__(other))

    def __mul__(self, other):
        return self.__class__(super().__mul__(other))

    def __rsub__(self, other):
        return self.__class__(super().__sub__(other))

    def __radd__(self, other):
        return self.__class__(super().__add__(other))

    def __rmul__(self, other):
        return self.__class__(super().__rmul__(other))   

Использование:

>>> size = get_folder_size("c:/users/tdavis/downloads")
>>> print(size)
5.81 GB
>>> size.GB
5.810891855508089
>>> size.gigabytes
5.810891855508089
>>> size.PB
0.005674699077644618
>>> size.MB
5950.353260040283
>>> size
ByteSize(6239397620)

Я также сталкивался с этим вопросом, в котором есть несколько более компактных и, возможно, более производительных стратегий для печати размеров файлов.

Ответ 17

для получения размера одного файла, существует os.path.getsize()

>>> import os
>>> os.path.getsize("/path/file")
35L

его сообщено в байтах.

Ответ 18

Я использую python 2.7.13 с scandir, и здесь моя однострочная рекурсивная функция, чтобы получить общий размер папка:

from scandir import scandir
def getTotFldrSize(path):
    return sum([s.stat(follow_symlinks=False).st_size for s in scandir(path) if s.is_file(follow_symlinks=False)]) + \
    + sum([getTotFldrSize(s.path) for s in scandir(path) if s.is_dir(follow_symlinks=False)])

>>> print getTotFldrSize('.')
1203245680

https://pypi.python.org/pypi/scandir

Ответ 19

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

Следующая функция вычисляет размер папки и всех ее подпапок.

import os

def folder_size(path):
    parent = {}  # path to parent path mapper
    folder_size = {}  # storing the size of directories
    folder = os.path.realpath(path)

    for root, _, filenames in os.walk(folder):
        if root == folder:
            parent[root] = -1  # the root folder will not have any parent
            folder_size[root] = 0.0  # intializing the size to 0

        elif root not in parent:
            immediate_parent_path = os.path.dirname(root)  # extract the immediate parent of the subdirectory
            parent[root] = immediate_parent_path  # store the parent of the subdirectory
            folder_size[root] = 0.0  # initialize the size to 0

        total_size = 0
        for filename in filenames:
            filepath = os.path.join(root, filename)
            total_size += os.stat(filepath).st_size  # computing the size of the files under the directory
        folder_size[root] = total_size  # store the updated size

        temp_path = root  # for subdirectories, we need to update the size of the parent till the root parent
        while parent[temp_path] != -1:
            folder_size[parent[temp_path]] += total_size
            temp_path = parent[temp_path]

    return folder_size[folder]/1000000.0

Ответ 20

Для чего это стоит... команда tree делает все это бесплатно:

tree -h --du /path/to/dir  # files and dirs
tree -h -d --du /path/to/dir  # dirs only

Мне нравится Python, но на сегодняшний день самым простым решением проблемы не требуется никакого нового кода.

Ответ 21

Если вы находитесь в ОС Windows, вы можете сделать:

установите модуль pywin32, запустив:

pip install pywin32

а затем кодировать следующее:

import win32com.client as com

def get_folder_size(path):
   try:
       fso = com.Dispatch("Scripting.FileSystemObject")
       folder = fso.GetFolder(path)
       size = str(round(folder.Size / 1048576))
       print("Size: " + size + " MB")
   except Exception as e:
       print("Error --> " + str(e))

Ответ 22

Вот одна строка, которая делает это рекурсивно (рекурсивная опция доступна в Python 3.5):

import os
import glob
print(sum(os.path.getsize(f) for f in glob.glob('**', recursive=True) if os.path.isfile(f))/(1024*1024))

Ответ 23

Этот script сообщает вам, какой файл является самым большим в CWD, а также сообщает вам, в какой папке находится файл. Этот script работает для меня на win8 и python 3.3.3 shell

import os

folder=os.cwd()

number=0
string=""

for root, dirs, files in os.walk(folder):
    for file in files:
        pathname=os.path.join(root,file)
##        print (pathname)
##        print (os.path.getsize(pathname)/1024/1024)
        if number < os.path.getsize(pathname):
            number = os.path.getsize(pathname)
            string=pathname


##        print ()


print (string)
print ()
print (number)
print ("Number in bytes")

Ответ 24

По общему признанию, это своего рода хакерство и работает только в Unix/Linux.

Он соответствует du -sb ., потому что на самом деле это оболочка Python bash, которая запускает команду du -sb ..

import subprocess

def system_command(cmd):
    """"Function executes cmd parameter as a bash command."""
    p = subprocess.Popen(cmd,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    stdout, stderr = p.communicate()
    return stdout, stderr

size = int(system_command('du -sb . ')[0].split()[0])

Ответ 25

Я немного опоздал (и новичок) здесь, но я решил использовать модуль подпроцесса и командную строку 'du' с Linux, чтобы получить точное значение для размера папки в МБ. Мне пришлось использовать if и elif для корневой папки, потому что в противном случае подпроцесс вызывает ошибку из-за ненулевого возвращаемого значения.

import subprocess
import os

#
# get folder size
#
def get_size(self, path):
    if os.path.exists(path) and path != '/':
        cmd = str(subprocess.check_output(['sudo', 'du', '-s', path])).\
            replace('b\'', '').replace('\'', '').split('\\t')[0]
        return float(cmd) / 1000000
    elif os.path.exists(path) and path == '/':
        cmd = str(subprocess.getoutput(['sudo du -s /'])). \
            replace('b\'', '').replace('\'', '').split('\n')
        val = cmd[len(cmd) - 1].replace('/', '').replace(' ', '')
        return float(val) / 1000000
    else: raise ValueError

Ответ 26

Получить размер каталога

Свойства раствора:

  • возвращает оба: видимый размер (количество байтов в файле) и фактическое дисковое пространство, используемое файлами.
  • считает жестко связанные файлы только один раз
  • подсчитывает символические ссылки так же, как это делает du
  • не использует рекурсию
  • использует st.st_blocks для используемого дискового пространства, поэтому работает только в Unix-подобных системах

Код:

import os


def du(path):
    if os.path.islink(path):
        return (os.lstat(path).st_size, 0)
    if os.path.isfile(path):
        st = os.lstat(path)
        return (st.st_size, st.st_blocks * 512)
    apparent_total_bytes = 0
    total_bytes = 0
    have = []
    for dirpath, dirnames, filenames in os.walk(path):
        apparent_total_bytes += os.lstat(dirpath).st_size
        total_bytes += os.lstat(dirpath).st_blocks * 512
        for f in filenames:
            fp = os.path.join(dirpath, f)
            if os.path.islink(fp):
                apparent_total_bytes += os.lstat(fp).st_size
                continue
            st = os.lstat(fp)
            if st.st_ino in have:
                continue  # skip hardlinks which were already counted
            have.append(st.st_ino)
            apparent_total_bytes += st.st_size
            total_bytes += st.st_blocks * 512
        for d in dirnames:
            dp = os.path.join(dirpath, d)
            if os.path.islink(dp):
                apparent_total_bytes += os.lstat(dp).st_size
    return (apparent_total_bytes, total_bytes)

Пример использования:

>>> du('/lib')
(236425839, 244363264)

$ du -sb /lib
236425839   /lib
$ du -sB1 /lib
244363264   /lib

Удобочитаемый размер файла

Свойства раствора:

Код:

def humanized_size(num, suffix='B', si=False):
    if si:
        units = ['','K','M','G','T','P','E','Z']
        last_unit = 'Y'
        div = 1000.0
    else:
        units = ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']
        last_unit = 'Yi'
        div = 1024.0
    for unit in units:
        if abs(num) < div:
            return "%3.1f%s%s" % (num, unit, suffix)
        num /= div
    return "%.1f%s%s" % (num, last_unit, suffix)

Пример использования:

>>> humanized_size(236425839)
'225.5MiB'
>>> humanized_size(236425839, si=True)
'236.4MB'
>>> humanized_size(236425839, si=True, suffix='')
'236.4M'

Ответ 27

Решение, которое работает на Python 3.6 с использованием pathlib.

from pathlib import Path

sum([f.stat().st_size for f in Path("path").glob("**/*")])

Ответ 28

Python 3. 6+ рекурсивный размер папки/файла с использованием os.scandir. Как мощный, как в ответ по @blakev, но короче и в ЭСПЦ стиле питона.

import os

def size(path, *, follow_symlinks=False):
    try:
        with os.scandir(path) as it:
            return sum(size(entry, follow_symlinks=follow_symlinks) for entry in it)
    except NotADirectoryError:
        return os.stat(path, follow_symlinks=follow_symlinks).st_size

Ответ 29

для python3. 5+

from pathlib import Path

def get_size(path):
    return sum(p.stat().st_size for p in Path(path).rglob('*'))

Ответ 30

def recursive_dir_size(path):
    size = 0

    for x in os.listdir(path):
        if not os.path.isdir(os.path.join(path,x)):
            size += os.stat(os.path.join(path,x)).st_size
        else:
            size += recursive_dir_size(os.path.join(path,x))

    return size

Я написал эту функцию, которая дает мне точный общий размер каталога, я пробовал другие решения для циклов с os.walk, но я не знаю, почему конечный результат всегда был меньше фактического размера (в Ubuntu 18 env), Должно быть, я сделал что-то не так, но кого это волнует, написал, что этот работает отлично