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


当前回答

到目前为止,建议的一些方法实现了递归,其他方法使用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

其他回答

到目前为止,建议的一些方法实现了递归,其他方法使用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

要获取一个文件的大小,可以使用os.path.getsize()

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

它以字节为单位报告。

Du默认情况下不遵循符号链接。这里没有答案,使用follow_symlinks=False。

下面是一个遵循du默认行为的实现:

def du(path) -> int:
    total = 0
    for entry in os.scandir(path):
        if entry.is_file(follow_symlinks=False):
            total += entry.stat().st_size
        elif entry.is_dir(follow_symlinks=False):
            total += du(entry.path)
    return total

测试:

class Test(unittest.TestCase):
    def test_du(self):
        root = '/tmp/du_test'
        subprocess.run(['rm', '-rf', root])
        test_utils.mkdir(root)
        test_utils.create_file(root, 'A', '1M')
        test_utils.create_file(root, 'B', '1M')
        sub = '/'.join([root, 'sub'])
        test_utils.mkdir(sub)
        test_utils.create_file(sub, 'C', '1M')
        test_utils.create_file(sub, 'D', '1M')
        subprocess.run(['ln', '-s', '/tmp', '/'.join([root, 'link']), ])
        self.assertEqual(4 << 20, util.du(root))

下面是一个递归函数(它递归地总结所有子文件夹及其各自文件的大小),返回的字节与在linux中运行“du -sb .”时完全相同(其中“。”表示“当前文件夹”):

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

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