Is there a way to list all the available Windows' drives?

Question:

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

Asked By: Electrons_Ahoy

||

Answers:

import win32api

drives = win32api.GetLogicalDriveStrings()
drives = drives.split('00')[:-1]
print drives

Adapted from:
http://www.faqts.com/knowledge_base/view.phtml/aid/4670

Answered By: Ayman Hourieh

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']
Answered By: RichieHindle

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''))
[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,"rootcimv2") 
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 
Answered By: John Fouhy

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?

Answered By: Pulec

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('00')[:-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(':\00')[:-1])))[-1]

I also chose the alphabet using map/range/ord/chr over using string since parts of string are deprecated.

Answered By: flutefreak7

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)]
Answered By: Barmaley

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.

Answered By: Sebastian Hietsch

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
Answered By: tiredgin

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.

Answered By: Tcll

On Windows you can do a os.popen

import os
print os.popen("fsutil fsinfo drives").readlines()
Answered By: user2015144

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%
Answered By: Tcll

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']
Answered By: PythonMan

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
Answered By: EGarbus

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:']

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:\']
Answered By: Jobin James

If you want only the letters for each drive, you can just:

from win32.win32api import GetLogicalDriveStrings


drives = [drive for drive in GetLogicalDriveStrings()[0]]
Answered By: 1schware

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:']
Answered By: Milad Shaker
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.