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

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

24 ответов


это бросается в подкаталоги:

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)
            total_size += os.path.getsize(fp)
    return total_size

print get_size()

и oneliner для удовольствия с помощью os.listdir (не включает подкаталоги):

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

ссылки:

os.путь.getsize не - дает размер в байтах

os.прогулка

Обновлено Использовать os.путь.getsize не, это яснее, чем использование ОС.stat ().st_size метод.

спасибо ghostdog74 за указание на это!

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

обновить 2018

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


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


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

размер рекурсивной папки 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

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

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

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

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()

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

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

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

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))])

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

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])

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

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

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


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

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

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

преобразовать в КБ, см. многоразовая библиотека, чтобы получить читаемую человеком версию размера файла? на


немного поздно на вечеринку, но в одной строке при условии, что у вас есть glob2 и очеловечить установлен. Обратите внимание, что в 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'

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

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 выше, как рекомендовал user2233949


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

pip install sh

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

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

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

pip install humanize

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

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

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

сообщается в байтах.


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

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

pip установить 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))

Это удобно:

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

этот скрипт сообщает вам, какой файл является самым большим в CWD, а также сообщает вам, в какой папке находится файл. Этот скрипт работает для меня на 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")

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

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


Я использую 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


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

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

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

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

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

Я люблю Python, но, безусловно, самое простое решение проблемы не требует нового кода.


Я немного опоздал (и новый) здесь, но я решил использовать модуль подпроцесса и командную строку " 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

import os

def get_size(path):
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(path):
        for f in filenames:
            if os.path.exists(fp):
                fp = os.path.join(dirpath, f)
                total_size += os.path.getsize(fp)

    return total_size   # in megabytes

спасибо monkut & troex! Это работает очень хорошо!