Powershell: Get all the available Drive letters

Viewed 60

Using Powershell I am seeking the inverse of the drive letters currently in use.

The following code In a .bat-file reveals the currently available drives.

SETLOCAL EnableDelayedExpansion
net use
SET DRVLST=
FOR %%p IN (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO if not exist %%p:\nul @set DRVLST=!DRVLST! %%p:
echo Available Drives: %DRVLST%
echo -------------------------------------------------------------------------------
net view %SERVERNAME%
ENDLOCAL

In Powershell the drive letters in use can be extracted and stored in a variable $drives_in_use for further processing.

$drives_in_use=(Get-PSDrive).Name -match '^[a-z]$'
write-host $drives_in_use

How to obtain the result to be the 'subtraction' of $drives_in_use from all drives A to Z?

$availableDrives = $allDrives - $drives_in_use

I would assume that a reverse-lookup regex might be the solution. But I don't know how to achieve that.

3 Answers

One approach would be to create an alphabet array and filter with the notcontains operator.

#Create alphabet array (https://www.rapidtables.com/code/text/ascii-table.html)
$alph = 65..90 | ForEach-Object {[char]$_}
$availableDrives = $alph | Where-Object { $drives_in_use -notcontains $_ }

If you're using PowerShell 6+ then you can create $alph like this

$alph = 'A'..'Z'

Another approach would be to use .Net like:

$availableDrives = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -split '' | Where-Object { $_ -notin ([System.IO.DriveInfo]::GetDrives().Name).Substring(0,1) }

or

$availableDrives = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.ToCharArray() | Where-Object { $_ -notin ([System.IO.DriveInfo]::GetDrives().Name).Substring(0,1) }

or

$availableDrives = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -split '' | Where-Object { $_ -notin [System.IO.Directory]::GetLogicalDrives().Substring(0,1) }

or use Get-CimInstance (slower)

$availableDrives = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -split '' | Where-Object { $_ -notin (Get-CimInstance -ClassName win32_logicaldisk).DeviceID.Substring(0,1) }

A simple solution using the Compare-Object cmdlet:

Compare-Object -PassThru (Get-PsDrive [A-Z]).Name ([char[]] 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')

In PowerShell (Core) 7+, which now supports [char] instances as the endpoints of .., the range operator, you can simplifiy to:

Compare-Object -PassThru (Get-PsDrive [A-Z]).Name ('A'..'Z')
  • Compare-Object only reports differences between the two collections by default, and in this case the first collection - all currently defined drives whose name is a single letter between A and Z - is by definition as subset of the second collection. Therefore, the result is those letters in the second collection that aren't present in the first collection, i.e. the unused drive letters.

  • -PassThru ensures that the differing objects are output as-is,[1] instead of the default behavior of being reported as the .InputObject property of [pscustomobject] wrappers, alongside a .SideIndicator property that signals what collection the object came from ('<=' indicating an object from the first collection, '=>' from the second).


[1] They are, however, decorated with an ETS .SideIndicator property.

Related