'test for free drive letter to map
I am mapping and un-mapping drives in my script. I ran into a need to determine if a drive letter is already mapped which is easy with test-path.
The problem is how do I go for the next available drive letter if there is one?
New-PSDrive -Name "S" -Root "\\<Name>\GYBWatch" -PSProvider "FileSystem" -Persist
Solution 1:[1]
[char[]] $taken = (Get-PSDrive -Name [A-Z]).Name
$nextAvailable = ([char[]] (65..90)).Where({ $_ -notin $taken }, 'First')[0]
if (-not $nextAvailable) { throw "No drive letters available." }
(Get-PSDrive -Name [A-Z])uses a wildcard expression to get information about all currently defined single-letter-name drives..Nameuses member-access enumeration to return an array of the names of all these drives, i.e. the taken letters as strings.- The
[char[]]type constraint applied to variable$takenconverts the array of single-letter strings to an array of[char]instances.
[char[]] (65..90)programmatically creates the array of (English) uppercase letters,'A', 'B', ..., 'Z', using.., the range operator. In PowerShell (Core) 7+ you can use character endpoints directly:'A'..'Z'. Note that PowerShell is case-insensitive in general, so the case of these letters is irrelevant.The
.Where()array method iterates over each letter and returns the first letter ('First') that satisfies the condition:$_ -notin $takenreturns$trueif the input letter at hand ($_) is not an element of (-notin) the$takenarray, i.e. it returns$trueif the letter isn't currently taken.Note that
[0]is applied to the.Where()call so as to ensure that the (at most) one and only result is treated as a scalar, which is necessary, because the.Where()and.ForEach()methods always return a collection. That said, in PowerShell the distinction between a scalar and a collection with a single element often doesn't matter.
Note: The above starts looking for available drive letters with letter A (drive A:).
To start looking from a different letter - say E - do the following:
Adjust the starting character in the letter-array-creation operation,
..[char[]] (69..90) # 'E', 'F', ..., 'Z'69, i.e. the decimal form of the Unicode code point of the uppercaseEletter, was obtained with[int] [char] 'E'Again, in PowerShell (Core) 7+ you could simply use
'E'..'Z'
As an optional optimization you could additionally adjust the wildcard pattern in the
Get-PSDrivecall:(Get-PSDrive -Name [E-Z]).Name
An alternative solution that avoids looping and linear searches in arrays:
Surprisingly, however, it is slower than the solution above, perhaps due to overhead of exception handling; in practice, both solutions will likely perform acceptably.
It takes advantage of the fact that you can pass multiple drive letters (names) to the
-Nameparameter; non-existent names trigger a non-terminating error that can be escalated to a terminating one with-ErrorAction Stop, which can then be trapped withtry { ... } catch { ... }.The offending name - the nonexistent drive letter - is reported in the error record's
.TargetObjectproperty (reported via the automatic$_variable in thecatchblock, as an[ErrorRecord]instance).
$nextAvailable =
try {
$null = Get-PSDrive -ErrorAction Stop -Name ([char[]] (65..90))
} catch {
$_.TargetObject
}
if (-not $nextAvailable) { throw "No drive letters available." }
Solution 2:[2]
#quick and dirty
#create Alphabet
$alphabet = @()
for ([byte]$c = [char]'A'; $c -le [char]'Z'; $c++){
$alphabet += [char]$c
}
[String]::Join(", ", $alphabet)
#get all Letter which are not in use
$alphabet | where { (Get-PSDrive -PSProvider FileSystem | select -ExpandProperty Name) -notcontains $_ }
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Mr. S. |
