Tag Archives: Array

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:


$letters=[char[]](67..90)

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.

#change the window title
$host.ui.RawUI.WindowTitle="Happy Valentine's Day"

#define an array of sentiments
$print=@("Be mine","Hey cutie","Be my valentine","I luv u","I$([char]3)U","Love",
"Romance","Kiss me","Hug me","My Girl","UR the 1","Love me tender","Luv2Luv",
"Hold Me","Sweetie","Honey","HunnyBunny","$([char]3)2$([char]3)","4Ever","XXOOXX")

#define the colors to use for the candy
$color="Cyan","Magenta","Yellow","White","Red","DarkRed","Green"

#enter a number greater than 100 for maximum impact
$count=Read-Host "How many sweethearts do you want?"

#create the candy elements
1..$count | foreach -begin {cls} -process {
Write-Host "$(get-random $print) " -foreground (get-random $color) -nonewline
} -end {write-host "`n"}

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.

[cc lang="PowerShell"]
-join [char[]](116,103,105,102)
[/cc]

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

[cc lang="PowerShell"]
PS C:\> [char]120
x
[/cc]

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.

[cc lang="PowerShell"]
Param(
[Parameter(Position=0)]
[ValidateNotNullOrEmpty()]
[string]$Text=”PowerShell Rocks!”,
[switch]$Scriptblock
)

#create CHAR mapping hash table
$map=@{}
33..125 | foreach { $map.Add([string]$_,[char]$_)}

#create an empty array to hold the CHAR values
$values=@()
$text.ToCharArray() | foreach {
#because $_ will change, save the current piped in character as a variable
$ltr=$_
[int]$i=($map.getEnumerator() | where {$_.value -ceq $ltr }).Name
#add the value to the array
$values+=$i
}

if ($ScriptBlock) {
#write a scriptblock to the pipeline
#create the command
$ofs=”,”
#create a variable so we variable expansion of $values
$t=”-join [char[]]($values)”

#put it back together as a script block and write to the pipeline
[scriptblock]::Create($t)
}
else {
#write value array
$values
}
[/cc]

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.

[cc lang="PowerShell"]
$map=@{}
33..125 | foreach { $map.Add([string]$_,[char]$_)}
[/cc]

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.

[cc lang="PowerShell"]
$text.ToCharArray() | foreach {
#because $_ will change, save the current piped in character as a variable
$ltr=$_
[/cc]

The script splits the string into a character array using the default delimter 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.

[cc lang="PowerShell"]
[int]$i=($map.getEnumerator() | where {$_.value -ceq $ltr }).Name
#add the value to the array
$values+=$i
[/cc]

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:

[cc lang="PowerShell"]
$sb={-join [char[]]($values)}
write $sb
[/cc]

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.

[cc lang="PowerShell"]
$ofs=”,”
#create a variable so we get variable expansion of $values
$t=”-join [char[]]($values)”
[/cc]

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:

[cc lang="PowerShell"]
#put it back together as a script block and write to the pipeline
[scriptblock]::Create($t)
[/cc]

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

[cc lang="DOS"]
PS C:\scripts> $a=.\Translate-ToChar.ps1
PS C:\scripts> $a
80
111
119
101
114
83
104
101
108
108
0
82
111
99
107
115
33
[/cc]

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

[cc lang="DOS"]
PS C:\scripts> $b=.\Translate-ToChar.ps1 -Scriptblock
PS C:\scripts> $b
-join [char[]](80,111,119,101,114,83,104,101,108,108,0,82,111,99,107,115,33)
[/cc]

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

Friday Fun Out-Rainbow

For my readers who are just discovering my Friday posts, let me remind you that these are not necessarily practical, production worthy PowerShell scripts and functions. They are meant to be fun, yet educational. For example, in today’s Friday Fun I have a function that takes string input and writes colored output to the console. So while you may not need the function itself, you might learn something about writing advanced functions, arrays, Write Host and more. So today is all about rainbows, unicorns and puppies. Continue reading

Out-Clip

I’ve started working on the 2nd edition of Managing Active Directory with Windows PowerShell: TFM. As with almost all of my writing projects it will be full of PowerShell code examples. In the past I’ve always relied on a manual copy and paste to add content to the manuscript. The PowerShell Community Extensions made this a little easier in the past with their Out-Clipboard cmdlet. That made it easier to grab the results so I could paste them in my Word document. But I also needed the PowerShell command which meant another copy and paste. What I really needed was a way to grab the expression AND the result and send both to the Windows clipboard. Thus was born Out-Clip.














































































































































































































































The function requires the Write-Clipboard cmdlet from the PowerShell Community Extensions. I use a Try/Catch block to verify the cmdlet exists in your current session. If it doesn’t, you get an error message and the pipeline is terminated. You’ll need to make sure you manually load the module before running this function.

The function itself is pretty basic I think. You can specify one or more scriptblocks either with the –Scriptblock parameter or piped into the function.

PS C:\> out-clip {get-service spooler | select *}

Since I prefer almost all of my code examples to use a prompt of PS C:\>, the function prepends it before the scriptblock command. If you prefer to see the actual or current location, use the –UseCurrentLocation parameter. If you prefer no prompt, then use –NoLocation. Because these two values are mutually exclusive, I’ve used two parameter sets, which you can see when you look at help.

PS C:\> get-help out-clip

NAME

Out-Clip

SYNOPSIS

Capture a PowerShell command and its result to the Windows clipboard.

SYNTAX

Out-Clip [-ScriptBlock] <ScriptBlock[]> [-UseCurrentLocation] [<CommonParameters>]

Out-Clip [-ScriptBlock] <ScriptBlock[]> [-NoLocation] [<CommonParameters>]

DESCRIPTION

Using Write-Clipboard from the PowerShell Community Extensions,

this function will take a Windows PowerShell scriptblock, execute

it and write both the command and the results to the Windows clipboard.

You can also pipe a collection of scriptblocks to this function or use

a comma separated list with the -Scriptblock parameter.

By default the output will include the PS prompt set to PS C:\>.

If you want to use the actual current location then specify

-UseCurrentLocation. If you prefer no prompt at all then use -NoLocation.

RELATED LINKS

Write-Clipboard

Set-Clipboard

Out-Clipboard

Get-Clipboard

REMARKS

To see the examples, type: “get-help Out-Clip -examples”.

For more information, type: “get-help Out-Clip -detailed”.

For technical information, type: “get-help Out-Clip -full”.

Out-Clip uses a hash-table to store the scriptblock command and the result. If the scriptblock fails, then the last error is grabbed and used instead for the result. Thus I always get the command and result in the clipboard, even if the command failed. After all scriptblocks have been evaluated the hash-table’s contents are piped to Write-Clipboard.














The script file also defines an alias, oc, for the function since I don’t like to type anymore than I have to.

Granted, this function meets a pretty specific need, but it does demonstrate some PowerShell 2.0 scripting features such as multiple parameter sets, Try/Catch and hash tables. If you try it out, I hope you’ll let me know what you think.