使用Python计算目录的大小?

Gar*_*hby 157 python directory

在我重新发明这个特定的轮子之前,有没有人有一个很好的例程来计算使用Python的目录大小?如果例程能够很好地格式化Mb/Gb等,那将是非常好的.

mon*_*kut 210

这抓住了子目录:

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')
Run Code Online (Sandbox Code Playgroud)

使用os.listdir(不包括子目录)的oneliner乐趣:

import os
sum(os.path.getsize(f) for f in os.listdir('.') if os.path.isfile(f))
Run Code Online (Sandbox Code Playgroud)

参考:

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

os.walk

更新 要使用os.path.getsize,这比使用os.stat().st_size方法更清晰.

感谢ghostdog74指出这一点!

os.stat - st_size以字节为单位给出大小.也可用于获取文件大小和其他文件相关信息.

更新2018年

如果您使用Python 3.4或之前的版本,那么您可以考虑使用walk第三方scandir软件包提供的更有效的方法.在Python 3.5及更高版本中,此软件包已合并到标准库中,并且os.walk已收到相应的性能提升.

  • 为了真正有趣,你可以在一行中做一个递归大小:sum(os.path.getsize(os.path.join(dirpath,filename))用于dirpath,dirnames,os.walk中的文件名(PATH)用于文件名中的文件名) (34认同)
  • +1但oneliner不返回有效结果,因为它不是递归的 (10认同)
  • 警告!这和'du -sb'不一样.查看Samuel Lampa的答案!您的代码忽略用于存储FAT的文件夹的大小. (3认同)
  • 是的,这只是针对平面目录的情况. (2认同)
  • 但是如果你不想遵循符号链接,你必须使用`st_size`,因为你应该使用`lstat`. (2认同)
  • 在许多情况下,代码会给出错误或至少意外的结果.例如`os.path.getsize('/ proc/kcore')`.它在我的系统上返回128Tb.我的`/ proc`目录> 128Tb的大小也是如此?这取决于.:) (2认同)
  • 一个衬垫不包括符号链接 `sum([os.path.getsize(fp) for fp in (os.path.join(dirpath, f) for dirpath, dirnames, filenames in os.walk(START_PATH) for f in filenames) if不是 os.path.islink(fp)])` ...很难阅读! (2认同)

fla*_*ier 36

到目前为止建议的一些方法实现递归,其他方法使用shell或不会产生整齐格式化的结果.当您的代码对于Linux平台来说是一次性的时,您可以像往常一样进行格式化,包括递归,作为单行.除了print在最后一行,它将适用于当前版本python2python3:

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('.'))
Run Code Online (Sandbox Code Playgroud)

简单,高效,适用于文件和多级目录:

$ chmod 750 du.py
$ ./du.py
2,9M
Run Code Online (Sandbox Code Playgroud)

5年后有点晚了,但因为这仍然是搜索引擎的热门列表,它可能会有所帮助......

  • Python本质上是跨平台的,应该可以回避这一点 (13认同)
  • 铌.仅限Linux. (11认同)
  • 谢谢你的评论.我在答案中添加了一些关于平台依赖性的警告.但是,如果是一次性脚本,很多Python代码.这样的代码不应该带有功能限制,冗长且容易出错的段落,或者在边缘情况下不常见的结果,只是为了便携性_超越任何需求_.这是一如既往的权衡,开发商有责任明智地选择;) (10认同)
  • Nitpick:不是Linux而是Unix/Posix特定:) (9认同)
  • 为了将搜索范围限制在文件系统中,在du命令中添加'-x'选项可能是明智的。换句话说,改用['du','-shx',path]。 (3认同)

Sam*_*mpa 24

这是一个递归函数(它递归地总结了所有子文件夹及其各自文件的大小),它返回与运行"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("."))
Run Code Online (Sandbox Code Playgroud)

  • 这个函数也计算符号链接的大小 - 如果你想跳过符号链接,你必须检查那不是:if os.path.isfile(itempath) 和 os.path.islink(itempath) 和 elif os.path.isdir( itempath) 和 os.path.islink(itempath)。 (3认同)

bla*_*kev 15

使用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
Run Code Online (Sandbox Code Playgroud)

  • @ weiji14丢失括号,即`sum(entry.stat().st_size for os.scandir(file))`.没有理由列出一个列表,因为`sum`也需要迭代器. (4认同)
  • Python 3 单行方法如果不担心递归性`sum([entry.stat().st_size for entry in os.scandir(file)])`。注意输出以字节为单位,/1024 获得 KB,/(1024*1024) 获得 MB。 (3认同)

Ter*_*vis 12

使用pathlib我想出了这个单行来获取文件夹的大小:

sum(file.stat().st_size for file in Path(folder).rglob('*'))
Run Code Online (Sandbox Code Playgroud)

这就是我想出的格式良好的输出:

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))   
Run Code Online (Sandbox Code Playgroud)

用法:

>>> 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)
Run Code Online (Sandbox Code Playgroud)

我也遇到了这个问题,它有一些更紧凑且可能更高效的打印文件大小的策略。


Chr*_*ris 8

接受的答案没有考虑硬链接或软链接,并会将这些文件计算两次.您需要跟踪您看到的哪些inode,而不是添加这些文件的大小.

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()
Run Code Online (Sandbox Code Playgroud)

  • 考虑使用`os.lstat`(而不是`os.stat`),它避免使用符号链接:[docs.python.org/2/library/os.html#os.lstat](http://docs.python) .org等/ 2 /库/ os.html#os.lstat) (5认同)

tro*_*oex 7

monknut答案是好的,但它在破坏的符号链接上失败,所以你还必须检查这个路径是否真的存在

if os.path.exists(fp):
    total_size += os.stat(fp).st_size
Run Code Online (Sandbox Code Playgroud)

  • 您可能不想遵循符号链接.你应该使用`lstat`. (3认同)

and*_*ewh 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
Run Code Online (Sandbox Code Playgroud)

  • 克里斯的回答也没有考虑符号链接和目录本身的大小.我已经相应地编辑了你的答案,固定功能的输出现在与`df -sb`相同. (2认同)

小智 7

一个递归的单行:

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))])
Run Code Online (Sandbox Code Playgroud)


Sar*_*ica 7

聚会有点晚了,但只要你安装了glob2humanize ,就可以在一行中找到。请注意,在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'
Run Code Online (Sandbox Code Playgroud)

  • 从 Python 3.5 开始,内置的 `glob` 支持递归。您可以使用: `glob.glob('/var/**', recursive=True)` (3认同)

Clo*_*ion 6

获取目录大小

溶液的性质:

  • 返回两者:表观大小(文件中的字节数)和文件使用的实际磁盘空间。
  • 仅对硬链接文件计数一次
  • 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)
Run Code Online (Sandbox Code Playgroud)

用法示例:

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

$ du -sb /lib
236425839   /lib
$ du -sB1 /lib
244363264   /lib
Run Code Online (Sandbox Code Playgroud)

人类可读的文件大小

溶液的性质:

代码:

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)
Run Code Online (Sandbox Code Playgroud)

用法示例:

>>> humanized_size(236425839)
'225.5MiB'
>>> humanized_size(236425839, si=True)
'236.4MB'
>>> humanized_size(236425839, si=True, suffix='')
'236.4M'
Run Code Online (Sandbox Code Playgroud)


Wak*_*eng 6

适用于python3.5+

from pathlib import Path

def get_size(path: str) -> int:
    return sum(p.stat().st_size for p in Path(path).rglob('*'))
Run Code Online (Sandbox Code Playgroud)

用法::

In [6]: get_size('/etc/not-exist-path')
Out[6]: 0
In [7]: get_size('.')
Out[7]: 12038689
In [8]: def filesize(size: int) -> str:
   ...:     for unit in ("B", "K", "M", "G"):
   ...:         if size < 1024:
   ...:             break
   ...:         size /= 1024
   ...:     return f"{size:.1f}{unit}"
   ...:

In [9]: filesize(get_size('.'))
Out[9]: '11.5M'

Run Code Online (Sandbox Code Playgroud)


Ali*_*MAR 5

你可以这样做:

import commands   
size = commands.getoutput('du -sh /path/').split()[0]
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我在返回结果之前没有测试结果,如果您愿意,可以使用commands.getstatusoutput检查它。


Aur*_*oms 5

对于问题的第二部分

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])
Run Code Online (Sandbox Code Playgroud)