Reputation: 64709
How do you use Python to determine which Linux device/partition contains a given filesystem?
e.g.
>>> get_filesystem_device('/')
/dev/sda
>>> get_filesystem_partition('/')
/dev/sda1
Upvotes: 8
Views: 10514
Reputation: 7382
You can do it without importing anything, though this only works for mounted partitions
mountpoint.py:
with open("/proc/mounts",'r') as mounts:
mountpoints = dict( line.split()[1::-1] for line in mounts )
print(mountpoints['/'])
output:
ws2:[tcll]:~$ python3 ./mountpoint.py
/dev/sda6
ws2:[tcll]:~$
If you want to include some unmounted partitions, you'll have to scan fstab, however this won't get target mountpoints for things like USB drives.
you can also:
import os
for label in os.listdir('/dev/disk/by-label'):
mountpoint = '/media/%s/%s'%( os.environ('USER'), label ) # not guaranteed
if mountpoint not in mountpoints:
mountpoints[mountpoint] = '/dev/%s'%(
os.readlink( '/dev/disk/by-label/%s'%label )[6:]
)
for partitions with labels that aren't in fstab.
Lastly, for partitions that don't have labels and aren't in fstab, you can also get their potential mountpoints:
for uuid in os.listdir('/dev/disk/by-uuid'):
mountpoint = '/run/media/%s/%s'%( os.environ('USER'), uuid ) # also not guaranteed
if mountpoint not in mountpoints:
mountpoints[mountpoint] = '/dev/%s'%(
os.readlink( '/dev/disk/by-uuid/%s'%uuid )[6:]
)
Sadly Linux doesn't make it easy to get details for unmounted partitions such as mountpoint and filesystem-type, you usually need to depend on some root program that can read /dev/sd(x)# to determine the filesystem, and need to guess the target mountpoint.
Upvotes: 1
Reputation: 834
My code
#file /dir1/check.py
import os
from subprocess import *
import subprocess
#function definition
def check():
device = subprocess.check_output("awk '$2 == \"/\" { print $1}' /proc/mounts", shell=True)
device_decode=device.decode()
print(device_decode[-5:-1])
print(device_decode[-2])
return
#function invocation
check()
Run in the bash shell
python3 /dir1/check.py
Upvotes: 0
Reputation: 6176
Here is how you can simply get the devices major and minor numbers:
import os
major, minor = divmod(os.stat('/').st_dev, 256)
Upvotes: 0
Reputation: 2360
How about using the (linux) blkid command (/sbin/blkid)
$ uname --kernel-name --kernel-release
Linux 3.2.0-4-amd64
$ python --version
Python 2.7.3
-
#!/usr/bin/env python
import subprocess
sys_command = "/sbin/blkid"
proc = subprocess.Popen(sys_command,
stdout=subprocess.PIPE,
shell=True)
# proc.communicate() returns a tuple (stdout,stderr)
blkid_output = proc.communicate()[0]
print blkid_output
Here's the output on a dual-boot laptop with an (unmounted) USB drive (sdb1)
$ ./blkid.py
/dev/sda1: LABEL="RECOVERY" UUID="xxxx-xxxx" TYPE="vfat"
/dev/sda2: LABEL="OS" UUID="xxxxxxxxxxxxxxx" TYPE="ntfs"
/dev/sda5: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4"
/dev/sda6: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="swap"
/dev/sda7: UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" TYPE="ext4"
/dev/sdb1: LABEL="CrunchBang" TYPE="iso9660"
Upvotes: 0
Reputation: 69
There are problems with quite a few of the above solutions. There's actually a problem with the question as well.
The last answer (searching /proc/mounts) just doesn't work: searching for "/" will match every line in /proc/mounts. Even correcting this like this won't work:
import subprocess
device = subprocess.check_output("awk '$2 == \"/filesystem\" { print $1}' /proc/mounts", shell=True)
print device
When "/filesystem" is "/" you'll typically get two entries, one for "rootfs" and one for the actual device. It also won't work when the mounted file system name has spaces in it (the space appears as \040 in /proc/mounts).
The problem is made worse with btrfs subvolumes. Each subvolume is mounted separately but they all share the same device. If you're trying to use a btrfs snapshot for backups (as I was) then you need the subvolume name and an indication of the filesystem type.
This function returns a tuple of (device, mountpoint, filesystem) and seems to work:
import os
def get_filesystem_partition(fs):
res = None
dev = os.lstat(fs).st_dev
for line in file('/proc/mounts'):
# lines are device, mountpoint, filesystem, <rest>
# later entries override earlier ones
line = [s.decode('string_escape') for s in line.split()[:3]]
if dev == os.lstat(line[1]).st_dev:
res = tuple(line)
return res
That seems to work for all the cases I can think of, although I expect that there are still pathological cases where it falls to bits.
Upvotes: 1
Reputation: 1922
I recently had a need for this solution also. After seeing all the convoluted methods of getting the result I wanted through pure python, I decided to turn to the shell for help.
import subprocess
device = subprocess.check_output("grep '/filesystem' /proc/mounts | awk '{printf $1}'", shell=True)
print device
This gives me exactly what I want, the device string for where my filesystem is mounted.
Short, sweet, and runs in python. :)
Upvotes: 1
Reputation: 3035
Your question was about Linux, so this is (more or less) linux specific.
Below is code example for three variants for mapping major/minor to a device name.
I'd say that /proc/partitions is simplest - it is just one file to open and check. hal gives you most information, and abstracts away lots of details. sysfs may be viewed as more correct that /proc/partitions and doesn't require hal to be running.
For a desktop program I would go for hal. On an embedded system I'd go with sysfs.
import os
def main():
dev = os.stat("/home/").st_dev
major, minor = os.major(dev), os.minor(dev)
print "/proc/partitions says:", ask_proc_partitions(major, minor)
print "HAL says:", ask_hal(major, minor)
print "/sys says:", ask_sysfs(major, minor)
def _parse_proc_partitions():
res = {}
for line in file("/proc/partitions"):
fields = line.split()
try:
tmaj = int(fields[0])
tmin = int(fields[1])
name = fields[3]
res[(tmaj, tmin)] = name
except:
# just ignore parse errors in header/separator lines
pass
return res
def ask_proc_partitions(major, minor):
d = _parse_proc_partitions()
return d[(major, minor)]
def ask_hal(major, minor):
import dbus
bus = dbus.SystemBus()
halobj = bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager')
hal = dbus.Interface(halobj, 'org.freedesktop.Hal.Manager')
def getdevprops(p):
bdevi = dbus.Interface(bus.get_object('org.freedesktop.Hal', p),
"org.freedesktop.Hal.Device")
return bdevi.GetAllProperties()
bdevs = hal.FindDeviceByCapability("block")
for bdev in bdevs:
props = getdevprops(bdev)
if (props['block.major'], props['block.minor']) == (major, minor):
parentprops = getdevprops(props['info.parent'])
return (str(props['block.device']),
str(parentprops['block.device']))
def ask_sysfs(major, minor):
from glob import glob
needle = "%d:%d" % (major, minor)
files = glob("/sys/class/block/*/dev")
for f in files:
if file(f).read().strip() == needle:
return os.path.dirname(f)
return None
if __name__ == '__main__':
main()
Upvotes: 3
Reputation: 103744
It is not the purdiest, but this will get you started:
#!/usr/bin/python
import os, stat, subprocess, shlex, re, sys
dev=os.stat('/')[stat.ST_DEV]
major=os.major(dev)
minor=os.minor(dev)
out = subprocess.Popen(shlex.split("df /"), stdout=subprocess.PIPE).communicate()
m=re.search(r'(/[^\s]+)\s',str(out))
if m:
mp= m.group(1)
else:
print "cannot parse df"
sys.exit(2)
print "'/' mounted at '%s' with dev number %i, %i" % (mp,major,minor)
On OS X:
'/' mounted at '/dev/disk0s2' with dev number 14, 2
On Ubuntu:
'/' mounted at '/dev/sda1' with dev number 8, 1
To get the device name, chop off the minor number from the partition name. On OS X, also chop the 's' + minor number.
Upvotes: 0
Reputation: 11524
It looks like this post has some of your answer (still not sure just how to grab the major/minor out of the /dev/sda2
entry to match it up with what os.stat()
returns for /
:
Device number in stat command output
>>> import os
>>> print hex(os.stat('/')[2])
0x802
\ \minor device number
\major device number
[me@server /]$ ls -l /dev/sda2
brw-rw---- 1 root disk 8, 2 Jun 24 2004 /dev/sda2
[me@server jgaines2]$ \ \minor device number
\major device number
Upvotes: 1