Friday Fun: Get Next Available Drive Letter

A few days ago I saw on question, I think on Facebook, about using PowerShell to find the next available drive letter that could be used for mapping a network drive. Before I show you my approach, let me state that if you need to map a drive in PowerShell for use only within your PowerShell solution, you don’t need a drive letter. You can name the drive anything you want when using New-PSDrive.

new-psdrive Backup -PSProvider Filesystem -Root "\\NAS01\backup"

But of course that won’t work if you plan on using the NET USE command. So let’s figure out how to determine the next available drive letter. First, let’s build an array of possible drive letters. I’m too lazy to type the letters C-Z (skipping A and B for the sake of nostalgia). So I’ll “create” them like this:


This creates an array of letters C-Z, albeit technically [CHAR] objects, but we’re good. Next we need a list of currently used drive letters which we can retrieve from WMI.

$devices=get-wmiobject win32_logicaldisk | select -expand DeviceID

I’m expanding the DeviceID property so $devices is a simple array and not a collection of objects with a DeviceID property. I did that so that I could use the -Contains operator.

$letters | where {$devices -notcontains "$($_):"} | Select -first 1

With this simple command I’m piping the collection of characters to Where-Object, testing if each letter (with the appended 🙂 is NOT found in the array $devices. The letters are already sorted so all I need to do is select the first 1 and that will be the next available drive letter in my current PowerShell session.

Before I let you go today, let me point out one thing: I could combine these two lines into a single PowerShell one-liner.

[char[]](67..90) | Where {(get-wmiobject win32_logicaldisk | select -expand DeviceID) -notcontains "$($_):"} | Select -first 1

I’ll get the same result…BUT…just because you can do something in PowerShell doesn’t mean you should. This one liner takes almost 2400MS to execute. But when I break it into two lines, as I showed you, I get the end result in 110MS which is a dramatic and noticeable difference. You can test for yourself using Measure-Command. The take-away is to not be afraid to use multiple commands, especially in a script. It may be faster and it will certainly be easier to understand.

Friday Fun PowerShell Valentines Day

With Valentine’s Day just around the corner, I thought I should help you out with your gift giving. Remember those bags of candy with the cute sayings like “Be Mine”? Here’s how you can create a “bag” of them using Windows PowerShell; perfect for that extra geeky significant other. Or maybe you’ll just have fun playing with this.

The first part of the script changes the console title. Nothing extra special there, in terms of PowerShell. Next, I’m defining an array of strings that will be “printed” on the “candy”. I’m also defining an array of colors that you can use with Write-Host. Some colors like Gray hardly seem romantic so I’m limiting my array to the “pretty” colors. Finally, the script prompts you for the number of candies to create.

The main part of the script is a Foreach-Object expression. I’m using the range operator (..) as a counter. Each number is piped to ForEach, but I’m not doing anything with it. What I am doing though is using other cmdlet parameters that you may not be used to seeing. Most of the time when we use ForEach-Object, the scriptblock is the -Process parameter value, which runs once for every object piped in. But in my script I’m also using the -Begin parameter. This scriptblockk executes once before any pipelined objects are processed. In my script I’m simply clearing the screen. In the process script block I’m using Write-Host to write a random string from the $Print array using a random foreground color from the $color array. I’m also using -NoNewLine because I want to fill the screen, (or bag in keeping with my analogy). If I didn’t, I’d get a list. But because I’m not writing a new line, when I’ve reached the maximum number of candies, the -End scriptblock runs which simply adds the necessary return.

Download Valentines.ps1

Happy Valentine’s Day.

Friday Fun What a CHAR!

Last week I posted a PowerShell snippet on Twitter. My original post piped an array of integers as [CHAR] type using an OFS. Don’t worry about that. As many people reminded me, it is much easier to use the -Join operator.

I’ll let you try that on your own. The [CHAR] type is used to represent a character as an integer value, like this:

For this week’s Friday Fun I thought it would be nice to translate a string of text into corresponding character values. It looks like a secret code! Or we could use the translation in a join scriptblock. So I put together a little script I call Translate-ToChar.ps1. The script takes a string of text and writes an array of [CHAR] objects.

The script begins by defining a map hash table for what I think are all characters you are likely to find on a US keyboard. These should be character values 33 through 125 which I get using the range (..) operator.

Each number is piped to ForEach object where I add it to the hash table. In order to get the hash table key to work properly I cast the number as a string and the hash table value is the same number cast as a [CHAR]. Now we can begin breaking the string apart and “translating” it.

The script splits the string into a character array using the default delimiter of a space. Because I’m going to be using a pipelined expression, I save the current letter as a variable. It will keep things straight in a moment.

The next step is to find the character in the hash table values. I found the best way to accomplish this was to call the hash table’s GetEnumerator() method. This way I can pipe it to Where-Object and find the corresponding key.

Notice I’m using the case sensitive -ceq operator. The Name property of the hash table enumerator is the key value, or in other words the corresponding [CHAR] integer. With me still? This value is added to an array for the final result. In fact the default is to simply write $values to the pipeline. But, I’ve included a -Scriptblock parameter to have the script write a scriptblock to the pipeline using the -Join operator I mentioned earlier. Now for the interesting part.

I have an array variable which needs to be expanded into the scriptblock. This won’t work:

I’ll end up with a scriptblock but have no way of resolving $values once the script ends. So instead I create a string with $values knowing that PowerShell will expand it. And because I want the array to be expanded as a comma separated string, I’ll specify the $ofs variable as a comma. The default is a space.

The variable $t is now a string with all of the integer values from $values as a comma separated list. I can turn this into scriptblock like this:

The scriptblock gets written to the pipeline. So, I could run the script like this:

Now I can use $a however I want. Or I could create a scriptblock.

I can invoke $b anytime I want to see the message.

While I don’t expect you to be running this in production I hope you picked up some tips on using hash tables, arrays, scriptblocks and casting variable types.

Download Translate-ToChar

Friday Fun PowerShell Pep Talk

Today’s Friday Fun is meant to help get you excited about the upcoming Scripting Games. I want to add a little pep to your PowerShell prompt. Perhaps it will even keep you motivated. What I have for you today are variety of prompt functions. Consider them variations on a theme. Continue reading

Friday Fun: Let’s Play Bingo!

Today Campers we’re playing Bingo. Or at least getting ready to. This week I have some PowerShell code that will create a BINGO card. For those of you outside of North America you might need to take a crash course on this game. But even if you don’t play, this article will demonstrate some useful PowerShell concepts and cmdlets like arrays. Get-Random and New-Object. Continue reading