Is there a way in Python to list all the currently in-use drive letters in a Windows system?
(My Google-fu seems to have let me down on this one)
A C++
equivalent: Enumerating all available drive letters in Windows
Is there a way in Python to list all the currently in-use drive letters in a Windows system?
(My Google-fu seems to have let me down on this one)
A C++
equivalent: Enumerating all available drive letters in Windows
Without using any external libraries, if that matters to you:
import string
from ctypes import windll
def get_drives():
drives = []
bitmask = windll.kernel32.GetLogicalDrives()
for letter in string.uppercase:
if bitmask & 1:
drives.append(letter)
bitmask >>= 1
return drives
if __name__ == '__main__':
print get_drives() # On my PC, this prints ['A', 'C', 'D', 'F', 'H']
import win32api
drives = win32api.GetLogicalDriveStrings()
drives = drives.split('\000')[:-1]
print drives
Adapted from: http://www.faqts.com/knowledge_base/view.phtml/aid/4670
Found this solution on Google, slightly modified from original. Seem pretty pythonic and does not need any "exotic" imports
import os, string
available_drives = ['%s:' % d for d in string.ascii_uppercase if os.path.exists('%s:' % d)]
I wrote this piece of code:
import os
drives = [ chr(x) + ":" for x in range(65,91) if os.path.exists(chr(x) + ":") ]
It's based on @Barmaley's answer, but has the advantage of not using the string
module, in case you don't want to use it. It also works on my system, unlike @SingleNegationElimination's answer.
Those look like better answers. Here's my hackish cruft
import os, re
re.findall(r"[A-Z]+:.*$",os.popen("mountvol /").read(),re.MULTILINE)
Riffing a bit on RichieHindle's answer; it's not really better, but you can get windows to do the work of coming up with actual letters of the alphabet
>>> import ctypes
>>> buff_size = ctypes.windll.kernel32.GetLogicalDriveStringsW(0,None)
>>> buff = ctypes.create_string_buffer(buff_size*2)
>>> ctypes.windll.kernel32.GetLogicalDriveStringsW(buff_size,buff)
8
>>> filter(None, buff.raw.decode('utf-16-le').split(u'\0'))
[u'C:\\', u'D:\\']
The Microsoft Script Repository includes this recipe which might help. I don't have a windows machine to test it, though, so I'm not sure if you want "Name", "System Name", "Volume Name", or maybe something else.
import win32com.client
strComputer = "."
objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator")
objSWbemServices = objWMIService.ConnectServer(strComputer,"root\cimv2")
colItems = objSWbemServices.ExecQuery("Select * from Win32_LogicalDisk")
for objItem in colItems:
print "Access: ", objItem.Access
print "Availability: ", objItem.Availability
print "Block Size: ", objItem.BlockSize
print "Caption: ", objItem.Caption
print "Compressed: ", objItem.Compressed
print "Config Manager Error Code: ", objItem.ConfigManagerErrorCode
print "Config Manager User Config: ", objItem.ConfigManagerUserConfig
print "Creation Class Name: ", objItem.CreationClassName
print "Description: ", objItem.Description
print "Device ID: ", objItem.DeviceID
print "Drive Type: ", objItem.DriveType
print "Error Cleared: ", objItem.ErrorCleared
print "Error Description: ", objItem.ErrorDescription
print "Error Methodology: ", objItem.ErrorMethodology
print "File System: ", objItem.FileSystem
print "Free Space: ", objItem.FreeSpace
print "Install Date: ", objItem.InstallDate
print "Last Error Code: ", objItem.LastErrorCode
print "Maximum Component Length: ", objItem.MaximumComponentLength
print "Media Type: ", objItem.MediaType
print "Name: ", objItem.Name
print "Number Of Blocks: ", objItem.NumberOfBlocks
print "PNP Device ID: ", objItem.PNPDeviceID
z = objItem.PowerManagementCapabilities
if z is None:
a = 1
else:
for x in z:
print "Power Management Capabilities: ", x
print "Power Management Supported: ", objItem.PowerManagementSupported
print "Provider Name: ", objItem.ProviderName
print "Purpose: ", objItem.Purpose
print "Quotas Disabled: ", objItem.QuotasDisabled
print "Quotas Incomplete: ", objItem.QuotasIncomplete
print "Quotas Rebuilding: ", objItem.QuotasRebuilding
print "Size: ", objItem.Size
print "Status: ", objItem.Status
print "Status Info: ", objItem.StatusInfo
print "Supports Disk Quotas: ", objItem.SupportsDiskQuotas
print "Supports File-Based Compression: ", objItem.SupportsFileBasedCompression
print "System Creation Class Name: ", objItem.SystemCreationClassName
print "System Name: ", objItem.SystemName
print "Volume Dirty: ", objItem.VolumeDirty
print "Volume Name: ", objItem.VolumeName
print "Volume Serial Number: ", objItem.VolumeSerialNumber
Here is another great solution if you want to list only drives on your disc and not mapped network drives. If you want to filter by different attributes just print drps.
import psutil
drps = psutil.disk_partitions()
drives = [dp.device for dp in drps if dp.fstype == 'NTFS']
On Windows you can do a os.popen
import os
print os.popen("fsutil fsinfo drives").readlines()
More optimal solution based on @RichieHindle
def get_drives():
drives = []
bitmask = windll.kernel32.GetLogicalDrives()
letter = ord('A')
while bitmask > 0:
if bitmask & 1:
drives.append(chr(letter) + ':\\')
bitmask >>= 1
letter += 1
return drives
here's a simpler version, without installing any additional modules or any functions. Since drive letters can't go beyond A and Z, you can search if there is path available for each alphabet, like below:
>>> import os
>>> for drive_letter in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':
if os.path.exists(f'{drive_letter}:'):
print(f'{drive_letter}:')
else:
pass
the one-liner:
>>> import os
>>> [f'{d}:' for d in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' if os.path.exists(f'{d}:')]
['C:', 'D:']
As part of a similar task I also needed to grab a free drive letter. I decided I wanted the highest available letter. I first wrote it out more idiomatically, then crunched it to a 1-liner to see if it still made sense. As awesome as list comprehensions are I love sets for this: unused=set(alphabet)-set(used)
instead of having to do unused = [a for a in aphabet if a not in used]
. Cool stuff!
def get_used_drive_letters():
drives = win32api.GetLogicalDriveStrings()
drives = drives.split('\000')[:-1]
letters = [d[0] for d in drives]
return letters
def get_unused_drive_letters():
alphabet = map(chr, range(ord('A'), ord('Z')+1))
used = get_used_drive_letters()
unused = list(set(alphabet)-set(used))
return unused
def get_highest_unused_drive_letter():
unused = get_unused_drive_letters()
highest = list(reversed(sorted(unused)))[0]
return highest
The one liner:
def get_drive():
highest = sorted(list(set(map(chr, range(ord('A'), ord('Z')+1))) -
set(win32api.GetLogicalDriveStrings().split(':\\\000')[:-1])))[-1]
I also chose the alphabet using map/range/ord/chr over using string since parts of string are deprecated.
Here's my higher-performance approach (could probably be higher):
>>> from string import ascii_uppercase
>>> reverse_alphabet = ascii_uppercase[::-1]
>>> from ctypes import windll # Windows only
>>> GLD = windll.kernel32.GetLogicalDisk
>>> drives = ['%s:/'%reverse_alphabet[i] for i,v in enumerate(bin(GLD())[2:]) if v=='1']
Nobody really uses python's performative featurability...
Yes, I'm not following Windows standard path conventions ('\\')...
In all my years of using python, I've had no problems with '/' anywhere paths are used, and have made it standard in my programs.
This code will return of list of drivenames and letters, for example:
['Gateway(C:)', 'EOS_DIGITAL(L:)', 'Music Archive(O:)']
It only uses the standard library. It builds on a few ideas I found above. windll.kernel32.GetVolumeInformationW() returns 0 if the disk drive is empty, a CD rom without a disk for example. This code does not list these empty drives.
These 2 lines capture the letters of all of the drives:
bitmask = (bin(windll.kernel32.GetLogicalDrives())[2:])[::-1] # strip off leading 0b and reverse
drive_letters = [ascii_uppercase[i] + ':/' for i, v in enumerate(bitmask) if v == '1']
Here is the full routine:
from ctypes import windll, create_unicode_buffer, c_wchar_p, sizeof
from string import ascii_uppercase
def get_win_drive_names():
volumeNameBuffer = create_unicode_buffer(1024)
fileSystemNameBuffer = create_unicode_buffer(1024)
serial_number = None
max_component_length = None
file_system_flags = None
drive_names = []
# Get the drive letters, then use the letters to get the drive names
bitmask = (bin(windll.kernel32.GetLogicalDrives())[2:])[::-1] # strip off leading 0b and reverse
drive_letters = [ascii_uppercase[i] + ':/' for i, v in enumerate(bitmask) if v == '1']
for d in drive_letters:
rc = windll.kernel32.GetVolumeInformationW(c_wchar_p(d), volumeNameBuffer, sizeof(volumeNameBuffer),
serial_number, max_component_length, file_system_flags,
fileSystemNameBuffer, sizeof(fileSystemNameBuffer))
if rc:
drive_names.append(f'{volumeNameBuffer.value}({d[:2]})') # disk_name(C:)
return drive_names
This will help to find valid drives in windows os
import os
import string
drive = string.ascii_uppercase
valid_drives = []
for each_drive in drive:
if os.path.exist(each_drive+":\\"):
print(each_drive)
valid_drives.append(each_drive+":\\")
print(valid_drives)
The output will be
C
D
E
['C:\\','D:\\','E:\\']
If you want only the letters for each drive, you can just:
from win32.win32api import GetLogicalDriveStrings
drives = [drive for drive in GetLogicalDriveStrings()[0]]
As I don't have win32api installed on my field of notebooks I used this solution using wmic:
import subprocess
import string
#define alphabet
alphabet = []
for i in string.ascii_uppercase:
alphabet.append(i + ':')
#get letters that are mounted somewhere
mounted_letters = subprocess.Popen("wmic logicaldisk get name", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
#erase mounted letters from alphabet in nested loop
for line in mounted_letters.stdout.readlines():
if "Name" in line:
continue
for letter in alphabet:
if letter in line:
print 'Deleting letter %s from free alphabet %s' % letter
alphabet.pop(alphabet.index(letter))
print alphabet
alternatively you can get the difference from both list like this simpler solution (after launching wmic subprocess as mounted_letters):
#get output to list
mounted_letters_list = []
for line in mounted_letters.stdout.readlines():
if "Name" in line:
continue
mounted_letters_list.append(line.strip())
rest = list(set(alphabet) - set(mounted_letters_list))
rest.sort()
print rest
both solutions are similiarly fast, yet I guess set list is better for some reason, right?
if you don't want to worry about cross platform issues, including those across python platforms such as Pypy, and want something decently performative to be used when drives are updated during runtime:
>>> from os.path import exists
>>> from sys import platform
>>> drives = ''.join( l for l in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' if exists('%s:/'%l) ) if platform=='win32' else ''
>>> drives
'CZ'
here's my performance test of this code:
4000 iterations; threshold of min + 250ns:
__________________________________________________________________________________________________________code___|_______min______|_______max______|_______avg______|_efficiency
⡇⠀⠀⢀⠀⠀⠀⠀⠀⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⣷⣷⣶⣼⣶⣴⣴⣤⣤⣧⣤⣤⣠⣠⣤⣤⣶⣤⣤⣄⣠⣦⣤⣠⣤⣤⣤⣤⣄⣠⣤⣠⣤⣤⣠⣤⣤⣤⣤⣤⣤⣄⣤⣤⣄⣤⣄⣤⣠⣀⣀⣤⣄⣤⢀⣀⢀⣠⣠⣀⣀⣤⣀⣠
drives = ''.join( l for l in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' if exists('%s:/'%l) ) if platform=='win32' else '' | 290.049ns | 1975.975ns | 349.911ns | 82.892%
List hard drives using command prompt windows in python
Similarly, you can do it for Linux with a few changes
import os,re
regex = r"([^\s]*:)"
driver = os.popen("wmic logicaldisk get name").read()
print(re.findall(regex, driver))
sample output:
['A:', 'C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'L:']
From python 3.12(Yet to release the final version) onwards, You can use os.listdrives()
which will return a list containing the names of drives on a Windows system. This function uses GetLogicalDriveStringsW
windows API under the hood.
>>> import os
>>> help(os.listdrives)
Help on built-in function listdrives in module nt:
listdrives()
Return a list containing the names of drives in the system.
A drive name typically looks like 'C:\\'.
>>> os.listdrives()
['C:\\']