I saw the Get-NextFreeDrive
function in this answer and I wondered if there was a more efficient way to do this. It appears that the function in the linked answ
I found to my own cost that the currently accepted answer (ls function:[d-z]: -n | ?{ !(test-path $_) } | random) can indeed return things like CD drives.
I've made this one to exclude any local drives from the array:
"$([char[]]([char]'D'..[char]'Z')|Where-Object {((Get-WmiObject -Class Win32_LogicalDisk).DeviceID).replace(':','') -notcontains $_ }|Select-Object -first 1):"
It will return the first available letter. If you'd prefer the last available letter just change Select-Object -first 1
to Select-Object -last 1
This seems a bit like a "me too" answer, but I noticed that all the other answers use -contains
or -notcontains
and I just didn't like those solutions. So this may not be terribly efficient, but I like it better. The purpose of this code (for me) was to find the first drive that I can use to create a drive mapping.
$FreeDrive=Get-PSDrive -PSProvider FileSystem | Select-Object -ExpandProperty Name | Where-Object { ($_ -ne "A") -and ($_ -ne "B") -and ($_ -ne "C") } | ForEach-Object { [System.Convert]::ToByte([System.Convert]::ToChar($_)) }
$FreeDrive=@($FreeDrive)
if (($FreeDrive.Count -eq 1) -and ($FreeDrive[0] -ne "Z")) { $FreeDrive=[System.Convert]::ToChar($FreeDrive[0]+1) }
$j=0
while ((($FreeDrive[$j]+1) -eq $FreeDrive[$j+1]) -and ($j -lt ($FreeDrive.Count-1))) { $j++ }
$FreeDrive=[System.Convert]::ToChar($FreeDrive[$j]+1)
$FreeDrive
just going to add one that works for remote drive letters $computer will be the input and $driveletter would contain the next available drive on the remote computer
67..90 | foreach {if(((GWmi win32_logicaldisk -computer $computer -Property DeviceID).deviceID).Substring(0,1) -notcontains [char]$_){$driveLetter = [char]$_; break}}
might be able to shorten that but at that length its clear to see whats going on
Here's what I came up with. I need the last available drive letter from A to Z.
$AllLetters = 65..90 | ForEach-Object {[char]$_ + ":"}
$UsedLetters = get-wmiobject win32_logicaldisk | select -expand deviceid
$FreeLetters = $AllLetters | Where-Object {$UsedLetters -notcontains $_}
$FreeLetters | select-object -last 1
My two cents:
get-wmiobject win32_logicaldisk | select -expand DeviceID -Last 1 |
% { [char]([int][char]$_[0] + 1) + $_[1] }
Range of valid [CHAR]
is 68..90
, adding a check if [char]$_[0] -gt 90
avoid unexpected results.
In case some unit is a mapped network drive it return always the major successive, ex.:
c: system drive
d: cd/dvd
r: network mapped drive
the command return s:
and not e:
as [string]
This give the first free drive letter ( a little ugly.. someone can do it better IMO):
$l = get-wmiobject win32_logicaldisk | select -expand DeviceID | % { $_[0] }
$s = [int][char]$l[0]
foreach ( $let in $l )
{
if ([int][char]$let -ne $s)
{
$ret = [char]$s +":"
break
}
$s+=1
}
$ret
I like this way, for the following reasons:
It finds the first non used driveletter and maps it, and then it is finished.
$share="\\Server\Share"
$drvlist=(Get-PSDrive -PSProvider filesystem).Name
Foreach ($drvletter in "DEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()) {
If ($drvlist -notcontains $drvletter) {
$drv=New-PSDrive -PSProvider filesystem -Name $drvletter -Root $share
break
}
}