在我重新发明这个特殊的轮子之前,有没有人有一个很好的用Python计算目录大小的例程?如果该例程能以Mb/Gb等格式格式化大小,那就太好了。


当前回答

获取目录大小

解决方案的性质:

返回两者:表观大小(文件中的字节数)和文件使用的实际磁盘空间。 硬链接文件只计算一次 计数符号链接的方式与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

人类可读的文件大小

解决方案的性质:

最高支持Yottabytes 支持SI单位或IEC单位 支持自定义后缀

代码:

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'

其他回答

这将遍历所有子目录;文件大小总和:

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

和一个在线的乐趣使用操作系统。listdir(不包括子目录):

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

参考:

os.path.getsize -以字节为单位给出大小 os.walk os.path.islink

更新 使用os.path。Getsize,这比使用os.stat()更清楚。st_size方法。

感谢ghostdog74指出这一点!

操作系统。stat - st_size以字节为单位给出大小。也可用于获取文件大小等文件相关信息。

import os

nbytes = sum(d.stat().st_size for d in os.scandir('.') if d.is_file())

更新2018

如果您使用的是Python 3.4或更早版本,那么您可以考虑使用第三方scandir包提供的更有效的walk方法。在Python 3.5及以后版本中,此包已合并到标准库和操作系统中。行走得到了相应的性能提升。

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

蒙纳特的答案是很好的,但它失败的符号链接,所以你还必须检查这个路径是否真的存在

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

这有点晚了,但只要安装了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'

到目前为止,建议的一些方法实现了递归,其他方法使用shell或不会生成格式整齐的结果。当您的代码对于Linux平台是一次性的,您可以像往常一样获得格式化,包括递归,作为一行程序。除了最后一行的输出,它将适用于当前版本的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

Python 3.6+递归文件夹/文件大小使用os.scandir。和@blakev的回答一样强大,但更短,采用EAFP python风格。

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