Home » Python » Find size and free space of the filesystem containing a given file

Find size and free space of the filesystem containing a given file

Posted by: admin November 30, 2017 Leave a comment

Questions:

I’m using Python 2.6 on Linux. What is the fastest way:

  • to determine which partition contains a given directory or file?

    For example, suppose that /dev/sda2 is mounted on /home, and /dev/mapper/foo is mounted on /home/foo. From the string "/home/foo/bar/baz" I would like to recover the pair ("/dev/mapper/foo", "home/foo").

  • and then, to get usage statistics of the given partition? For example, given /dev/mapper/foo I would like to obtain the size of the partition and the free space available (either in bytes or approximately in megabytes).

Answers:

If you need the device name and mount point associated with the file, you should call an external program to get this information. df will provide all the information you need — when called as df filename it prints a line about the partition that contains the file.

To give an example:

import subprocess
df = subprocess.Popen(["df", "filename"], stdout=subprocess.PIPE)
output = df.communicate()[0]
device, size, used, available, percent, mountpoint = \
    output.split("\n")[1].split()

If you don’t need device name and mount point, going with os.statvfs() will be better (see other answers).

Questions:
Answers:

This doesn’t give the name of the partition, but you can get the filesystem statistics directly using the statvfs Unix system call. To call it from Python, use os.statvfs('/home/foo/bar/baz').

The relevant fields in the result, according to POSIX:

unsigned long f_frsize   Fundamental file system block size. 
fsblkcnt_t    f_blocks   Total number of blocks on file system in units of f_frsize. 
fsblkcnt_t    f_bfree    Total number of free blocks. 
fsblkcnt_t    f_bavail   Number of free blocks available to 
                         non-privileged process.

So to make sense of the values, multiply by f_frsize:

import os
statvfs = os.statvfs('/home/foo/bar/baz')

statvfs.f_frsize * statvfs.f_blocks     # Size of filesystem in bytes
statvfs.f_frsize * statvfs.f_bfree      # Actual number of free bytes
statvfs.f_frsize * statvfs.f_bavail     # Number of free bytes that ordinary users
                                      # are allowed to use (excl. reserved space)

Questions:
Answers:
import os

def get_mount_point(pathname):
    "Get the mount point of the filesystem containing pathname"
    pathname= os.path.normcase(os.path.realpath(pathname))
    parent_device= path_device= os.stat(pathname).st_dev
    while parent_device == path_device:
        mount_point= pathname
        pathname= os.path.dirname(pathname)
        if pathname == mount_point: break
        parent_device= os.stat(pathname).st_dev
    return mount_point

def get_mounted_device(pathname):
    "Get the device mounted at pathname"
    # uses "/proc/mounts"
    pathname= os.path.normcase(pathname) # might be unnecessary here
    try:
        with open("/proc/mounts", "r") as ifp:
            for line in ifp:
                fields= line.rstrip('\n').split()
                # note that line above assumes that
                # no mount points contain whitespace
                if fields[1] == pathname:
                    return fields[0]
    except EnvironmentError:
        pass
    return None # explicit

def get_fs_freespace(pathname):
    "Get the free space of the filesystem containing pathname"
    stat= os.statvfs(pathname)
    # use f_bfree for superuser, or f_bavail if filesystem
    # has reserved space for superuser
    return stat.f_bfree*stat.f_bsize

Some sample pathnames on my computer:

path 'trash':
  mp /home /dev/sda4
  free 6413754368
path 'smov':
  mp /mnt/S /dev/sde
  free 86761562112
path '/usr/local/lib':
  mp / rootfs
  free 2184364032
path '/proc/self/cmdline':
  mp /proc proc
  free 0

PS

if on Python ≥3.3, there’s shutil.disk_usage(path) which returns a named tuple of (total, used, free) expressed in bytes.

Questions:
Answers:

This should make everything you asked:

import os
from collections import namedtuple

disk_ntuple = namedtuple('partition',  'device mountpoint fstype')
usage_ntuple = namedtuple('usage',  'total used free percent')

def disk_partitions(all=False):
    """Return all mountd partitions as a nameduple.
    If all == False return phyisical partitions only.
    """
    phydevs = []
    f = open("/proc/filesystems", "r")
    for line in f:
        if not line.startswith("nodev"):
            phydevs.append(line.strip())

    retlist = []
    f = open('/etc/mtab', "r")
    for line in f:
        if not all and line.startswith('none'):
            continue
        fields = line.split()
        device = fields[0]
        mountpoint = fields[1]
        fstype = fields[2]
        if not all and fstype not in phydevs:
            continue
        if device == 'none':
            device = ''
        ntuple = disk_ntuple(device, mountpoint, fstype)
        retlist.append(ntuple)
    return retlist

def disk_usage(path):
    """Return disk usage associated with path."""
    st = os.statvfs(path)
    free = (st.f_bavail * st.f_frsize)
    total = (st.f_blocks * st.f_frsize)
    used = (st.f_blocks - st.f_bfree) * st.f_frsize
    try:
        percent = ret = (float(used) / total) * 100
    except ZeroDivisionError:
        percent = 0
    # NB: the percentage is -5% than what shown by df due to
    # reserved blocks that we are currently not considering:
    # http://goo.gl/sWGbH
    return usage_ntuple(total, used, free, round(percent, 1))


if __name__ == '__main__':
    for part in disk_partitions():
        print part
        print "    %s\n" % str(disk_usage(part.mountpoint))

On my box the code above prints:

[email protected]:~/dev$ python foo.py 
partition(device='/dev/sda3', mountpoint='/', fstype='ext4')
    usage(total=21378641920, used=4886749184, free=15405903872, percent=22.9)

partition(device='/dev/sda7', mountpoint='/home', fstype='ext4')
    usage(total=30227386368, used=12137168896, free=16554737664, percent=40.2)

partition(device='/dev/sdb1', mountpoint='/media/1CA0-065B', fstype='vfat')
    usage(total=7952400384, used=32768, free=7952367616, percent=0.0)

partition(device='/dev/sr0', mountpoint='/media/WB2PFRE_IT', fstype='iso9660')
    usage(total=695730176, used=695730176, free=0, percent=100.0)

partition(device='/dev/sda6', mountpoint='/media/Dati', fstype='fuseblk')
    usage(total=914217758720, used=614345637888, free=299872120832, percent=67.2)

Questions:
Answers:

For the first point, you can try using os.path.realpath to get a canonical path, check it against /etc/mtab (I’d actually suggest calling getmntent, but I can’t find a normal way to access it) to find the longest match. (to be sure, you should probably stat both the file and the presumed mountpoint to verify that they are in fact on the same device)

For the second point, use os.statvfs to get block size and usage information.

(Disclaimer: I have tested none of this, most of what I know came from the coreutils sources)

Questions:
Answers:

The simplest way to find out it.

import os
from collections import namedtuple

DiskUsage = namedtuple('DiskUsage', 'total used free')

def disk_usage(path):
    """Return disk usage statistics about the given path.

    Will return the namedtuple with attributes: 'total', 'used' and 'free',
    which are the amount of total, used and free space, in bytes.
    """
    st = os.statvfs(path)
    free = st.f_bavail * st.f_frsize
    total = st.f_blocks * st.f_frsize
    used = (st.f_blocks - st.f_bfree) * st.f_frsize
    return DiskUsage(total, used, free)

Questions:
Answers:

As of Python 3.3, there an easy and direct way to do this with the standard library:

┌─[[email protected]] - [~/temp] - [Fri Sep 30, 01:38]
└─[$]> cat free_space.py 
#!/usr/bin/env python3

import shutil

total, used, free = shutil.disk_usage(__file__)
print(total, used, free)
┌─[[email protected]] - [~/temp] - [Fri Sep 30, 01:38]
└─[$]> ./free_space.py 
1007870246912 460794834944 495854989312

These numbers are in bytes. See the documentation for more info.

Questions:
Answers:

Usually the /proc directory contains such information in Linux, it is a virtual filesystem. For example, /proc/mounts gives information about current mounted disks; and you can parse it directly. Utilities like top, df all make use of /proc.

I haven’t used it, but this might help too, if you want a wrapper: http://bitbucket.org/chrismiles/psi/wiki/Home

Questions:
Answers:
def disk_stat():
    import os
    hd={}
    disk = os.statvfs('/usr')
    percent = (disk.f_blocks - disk.f_bfree) * 100 / (disk.f_blocks -disk.f_bfree + disk.f_bavail) + 1
    return percent


print disk_stat('/')
print disk_stat('/data')

Questions:
Answers:

You can use popen() function from os. First of all, you should import os then use popen() function like following sample.
Also, you can get each part of command separately or get all part in one line.

#!/usr/bin/env python
import os, sys

if len(sys.argv) == 1:
   Partition_Name = "/dev/sda1"
elif len(sys.argv) == 2:
    Partition_Name = sys.argv[1]
else:
    print("Error: Much more parameter!")
    exit(0)

os.system('clear')

#Get each element separately:
Partition_Size = os.popen('df -h | grep ' + Partition_Name + ' |awk \'{ print $2 }\'').read()
Partition_Used =  os.popen('df -h | grep ' + Partition_Name + ' |awk \'{ print $3 }\'').read()
Partition_Free =  os.popen('df -h | grep ' + Partition_Name + ' |awk \'{ print $4 }\'').read()
Partition_UsedPercentage =  os.popen('df -h | grep ' + Partition_Name + ' |awk \'{ print $5 }\'').read()
Partition_MountPoint =  os.popen('df -h | grep ' + Partition_Name + ' |awk \'{ print $6 }\'').read()
#Get all elements:
AllInOne =  os.popen('df -h | grep ' + Partition_Name + ' |awk \'{ print "Size: " $2 "\t" "Used: " $3 "\t" "Free: " $4 "\t" "Use%: " $5 "\t" "Mounted on: " $6 }\'').read()

print("Partition Name: %s \n" % Partition_Name)
print(" Size: \t\t\t%s Used: \t\t\t%s Free: \t\t\t%s Used Percentage: \t%s" %(Partition_Size, Partition_Used, Partition_Free, Partition_UsedPercentage))
print("All in one: \n" + AllInOne)

Testing on Linux:

chmod +x showdfDetailes.py

Use this:

./showdfDetailes.py /dev/sda1

or use this:

./showdfDetailes.py

Output:

Partition Name: /dev/sda1

Size: 235G

Used: 3.3G

Free: 220G

Used Percentage: 2%

All in one:

Size: 235G Used: 3.3G Free: 220G Use%: 2% Mounted on: /