87 votes

Trouver la taille et l'espace libre du système de fichiers contenant un fichier donné

J'utilise Python 2.6 sous Linux. Quel est le moyen le plus rapide :

  • pour déterminer quelle partition contient un répertoire ou un fichier donné ?

    Par exemple, supposons que /dev/sda2 est monté sur /home et /dev/mapper/foo est monté sur /home/foo . De la chaîne "/home/foo/bar/baz" Je voudrais récupérer la paire ("/dev/mapper/foo", "home/foo") .

  • et ensuite, pour obtenir des statistiques d'utilisation de la partition donnée ? Par exemple, étant donné /dev/mapper/foo Je voudrais obtenir la taille de la partition et l'espace libre disponible (soit en octets, soit approximativement en mégaoctets).

0 votes

Prenez-vous en compte les liens symboliques ? Alors que vous pouvez avoir /home et /mnt/somedisk, /home/foo/x peut être un lien symbolique vers le répertoire /mnt/somedisk/xyzzy - il apparaît donc sous /home, mais vit en réalité dans /mnt/somedisk.

0 votes

@Piskvor : Non - pour le moment, je n'ai pas besoin de suivre les liens symboliques, ce sont juste des répertoires ordinaires. La première question demande essentiellement "trouver le répertoire ancêtre le plus proche qui a une partition montée sur lui".

0 votes

142voto

Mechanical snail Points 8589

Cela ne donne pas le nom de la partition, mais vous pouvez obtenir les statistiques du système de fichiers directement en utilisant la commande statvfs Appel système Unix. Pour l'appeler depuis Python, utilisez os.statvfs('/home/foo/bar/baz') .

Les champs pertinents dans le résultat, conformément à 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.

Pour donner un sens aux valeurs, il faut donc multiplier par 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)

0 votes

Je viens d'avoir un échec sur un système embarqué avec des ubifs. Il en résulte 100MB libres alors que seulement 10 étaient disponibles. Je ne sais pas d'où viennent les 100.

51voto

Sven Marnach Points 133943

Si vous n'avez besoin que de l'espace libre d'un appareil, consultez la réponse en utilisant la fonction os.statvfs() ci-dessous.

Si vous avez également besoin du nom du périphérique et du point de montage associé au fichier, vous devez appeler un programme externe pour obtenir ces informations. df fournira toutes les informations dont vous avez besoin -- lorsqu'il est appelé en tant que df filename il imprime une ligne sur la partition qui contient le fichier.

Pour donner un exemple :

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

Notez que cette méthode est plutôt fragile, car elle dépend du format exact de l'objet df mais je n'ai pas connaissance d'une solution plus robuste. (Il existe quelques solutions s'appuyant sur la fonction /proc système de fichiers ci-dessous qui sont encore moins portables que celui-ci).

1 votes

Spécifiquement, il pourrait faire import commands, puis commands.getoutput("df filename | tail -1 | gawk '{ print $6 }' ")

8 votes

El commands est remplacé par subprocess . Et je ne ferais pas l'analyse de la sortie en bash quand je peux le faire en Python :)

4 votes

Je ne connaissais pas l'argument "nom de fichier" de df. "df -B MB nom de fichier" fera l'affaire. Merci beaucoup.

26voto

tzot Points 32224
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

Quelques exemples de noms de chemin sur mon ordinateur :

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

si sur Python ≥3.3, il y a shutil.disk_usage(path) qui renvoie un tuple nommé de (total, used, free) exprimée en octets.

0 votes

Comme indiqué ci-dessus : Je viens de voir cette méthode utilisant statvfs échouer sur un système embarqué avec ubifs. Il en résulte 100 Mo libres alors que seulement 10 étaient disponibles. Je ne suis pas sûr de l'origine des 100.

16voto

Giampaolo Rodolà Points 2965

Cela devrait faire tout ce que vous avez demandé :

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

Sur ma boîte, le code ci-dessus s'imprime :

giampaolo@ubuntu:~/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)

1 votes

Jetez également un coup d'œil à cette recette : code.activestate.com/recipes/577972-disk-usage

0 votes

Un petit problème - all est une fonction intégrée et ne doit pas être utilisée comme variable dans une fonction.

0 votes

Peut-on le représenter en gigaoctets ?

6voto

Hasturkun Points 18653

Pour le premier point, vous pouvez essayer d'utiliser os.path.realpath pour obtenir un chemin canonique, vérifiez-le par rapport à /etc/mtab (En fait, je suggère d'appeler getmntent mais je n'arrive pas à trouver un moyen normal d'y accéder) pour trouver la correspondance la plus longue. (pour être sûr, vous devriez probablement stat à la fois le fichier et le point de montage présumé pour vérifier qu'ils se trouvent bien sur le même périphérique)

Pour le deuxième point, utilisez os.statvfs pour obtenir des informations sur la taille des blocs et leur utilisation.

(Disclaimer : Je n'ai rien testé de tout cela, la plupart de ce que je sais provient des sources coreutils)

0 votes

Re getmntent : bien, il y a toujours la possibilité de import ctypes; ctypes.cdll.LoadLibrary("libc.so.6").getmntent mais ce n'est pas si simple

0 votes

Je suis curieux de savoir pourquoi ce texte a reçu un downvote, un commentaire aurait été apprécié.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X