2009-09-08 196 views
103

在我重新發明這個特定的輪子之前,有沒有人用Python計算一個目錄的大小?這將是非常不錯的,如果該程序會在MB/GB等良好的格式化大小使用Python計算目錄的大小?

+11

這不會很好。你應該有一個函數來計算大小和一個完全獨立的函數(例如,也可以用於內存大小),以「在Mb/Gb等中很好地格式化大小」。 – 2010-02-15 02:37:08

+11

是的,我知道,但這節省了兩個問題。 – 2010-02-15 20:06:08

+1

* nix系統上的'tree'命令可以免費完成所有這些操作。 'tree -h -d --du/path/to/dir'。 – meh 2017-07-25 18:07:01

回答

144

這抓起子目錄:

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

而且使用os.listdir一個有趣的oneliner(不包括子目錄 ):

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

參考:

os.path.getsize - 給人的SIZ E在字節

os.walk

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

感謝ghostdog74指出這一點!

os.stat - st_size以字節爲單位給出大小。也可以用來獲取文件大小和其他文件相關的信息。

更新2015

scandir可用,並且可以比os.walk方法更快。包是可以從PyPI中,並os.scandir()要包含在Python 3.5:

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

+7

+1,但oneliner不返回有效的結果,因爲它不是遞歸的 – luc 2009-09-08 10:19:12

+2

是的,它只是用於平面目錄的情況。 – monkut 2009-09-08 10:23:08

+31

真正的樂趣,你可以做一個遞歸大小在一行: sum(os.path.getsize(os.path.join(dirpath,filename)) dirpath,dirnames,os.walk中的文件名(路徑) for filename in filenames) – driax 2010-08-29 20:02:39

7

monknut答案是好的,但它不能在破符號鏈接,所以你也必須檢查這條道路確實存在

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

你可能不想遵循符號鏈接。你應該使用'lstat'。 – asmeurer 2014-03-18 20:44:07

0

爲獲得一個文件的大小,有os.path.getsize()

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

其報道字節。

22

這是一個遞歸函數(遞歸地總結所有子文件夾及其各自文件的大小),它返回與運行「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(".")) 
+1

謝謝!很有用。 – 2011-02-27 01:59:17

+0

此函數計算的符號鏈接的尺寸過 - 如果你想跳過符號鏈接,你必須檢查,這不是說: 如果os.path.isfile(itempath)和os.path.islink(itempath) 和 elif os.path.isdir(itempath)和os.path.islink(itempath)。 – airween 2015-08-23 19:02:34

7

接受的答案並沒有考慮到硬或軟鏈接,並且將兩次計算這些文件。你想跟蹤你已經看到的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() 
+4

考慮使用'os.lstat'(而不是'os.stat'),它避免了以下符號鏈接:[docs.python.org/2/library/os.html#os.lstat](http://docs。 python.org/2/library/os.html#os.lstat) – 2014-01-30 11:22:01

4

你可以做這樣的事情:

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

在這種情況下,我回來了,如果你想你可以用commands.getstatusoutput檢查之前沒有測試結果。

6

克里斯的回答是不錯的,但可以通過使用一組檢查看到目錄,這也避免了使用異常的控制流進行更地道:

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 
+2

Chris的回答也沒有考慮符號鏈接和目錄本身的大小。我已經編輯了相應的答案,固定函數的輸出現在與'df -sb'完全相同。 – Creshal 2013-12-11 13:21:23

5

遞歸一行代碼:

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

雖然不是一個班輪...... – 2013-09-12 12:28:46

+1

雖然它不是一個班輪。但是,它會以字節爲單位遞歸計算文件夾大小(即使文件夾內有多個文件夾)並給出正確的值。 – Venkatesh 2014-12-18 19:57:41

-1
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!這工作真的很好!

+0

此代碼不會運行(f /​​ fp變量中的拼寫錯誤)並且不是遞歸的。 – oferlivny 2015-09-16 15:06:01

+0

這不會運行。在分配之前你指的是'fp'。它還會以字節返回'total_size',而不是兆字節。 – freethebees 2018-02-27 10:20:32

0

該腳本告訴你哪個文件是CWD中最大的文件,並告訴你文件在哪個文件夾中。 這個腳本對我的作品上的Win8和Python 3.3.3外殼

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") 
23

一些建議,到目前爲止實現遞歸的方法,另一些採用外殼或不會產生整齊格式的結果。當您的代碼是Linux平臺的一次性代碼時,您可以照常格式化,包括遞歸,作爲一行代碼。

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

簡單,高效,將文件和多級目錄的工作:除了在最後一行print,它將爲python2python3當前版本的工作

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

有點晚5年後,但因爲這仍然在搜索引擎的hitlist中,它可能會有所幫助...

+7

Nb。僅限Linux。 – meawoppl 2015-05-18 17:41:30

+7

Python,本質上是跨平臺的,可能應該避開這個 – 2015-09-11 23:48:52

+5

感謝這些評論。我添加了一些關於平臺依賴性的答案。但是,如果是一次性腳本,大部分Python代碼。這樣的代碼不應該帶有功能限制,冗長且容易出錯的段落,或者在邊緣情況下出現罕見結果,只是爲了便於攜帶_超出任何需求。它一如既往,是一種權衡,開發人員有責任明智地選擇; – flaschbier 2015-09-18 04:51:16

4

對於問題的第二部分

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

一襯你說...... 這裏是一個班輪:

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

雖然我可能會分裂出來,它不執行檢查。

要轉換爲KB看到Reusable library to get human readable version of file size?

2

工作,它的所有子目錄下面的腳本打印目錄大小爲指定的目錄。它也嘗試從緩存遞歸函數的調用中獲益(如果可能的話)。如果省略參數,該腳本將在當前目錄中工作。輸出按目錄大小從大到小排序。所以你可以根據你的需要來調整它。

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) 

編輯:移動上述null_decorator,如user2233949推薦

+0

你的腳本運行良好,但你需要移動'if sys.version_info> = ...'行上面的null_decorator函數。否則,你會得到一個'null_decorator'未定義的異常。儘管如此,它仍然很棒。 – user2233949 2016-02-02 23:26:53

+0

@ user2233949,謝謝!我相應地修改了代碼。 – MaxU 2016-02-02 23:51:04

10

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 
+1

如果不擔心遞歸性sum([entry.stat()。st_size用於os.scandir(file)])中的條目,Python 3單線程方法。注意輸出是以字節/ 1024來獲得KB和/(1024 * 1024)來獲得MB。 – weiji14 2017-10-31 21:43:08

+2

@ weiji14丟失括號,即'sum(entry.stat()。st_size用於os.scandir(file)中的條目)'。沒有理由做一個列表,因爲'sum'也需要迭代器。 – 2017-11-17 10:16:39

2

晚會有點晚,但只有一行,前提是您安裝了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' 
0

不可否認,這是一種黑客行爲,只適用於Unix/Linux。

它匹配du -sb .,因爲實際上這是一個運行du -sb .命令的Python bash包裝器。

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

我使用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

1

利用圖書館sh:模塊du這樣做:

pip install sh 

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

如果您想通過asterix,請使用glob,如here所述。

的值轉換在人類readables,使用humanize

pip install humanize 

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

當子目錄的大小計算,它應該更新其父母的文件夾的大小,並直到它到達根部父,這將繼續下去。

以下函數計算文件夾及其所有子文件夾的大小。

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 
0

對於它的價值......樹命令做這一切免費:

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

我喜歡Python,但迄今爲止這一問題最簡單的解決方案不需要新的代碼。