Training Down Under

I will be doing a private #PowerShell training class in Canberra, Australia in March. Since it is a long trek I’d love to add a second week of work, either training or speaking. I had a second training gig lined up but it fell through. I realize it might be short notice but I’d love to find someone who would like 3-5 days of training the week of March 26, 2012. I’m also open to speaking engagements if I can at least get my expenses covered. I’m willing to travel anywhere in the region: Australia, New Zealand, Indonesia or even Singapore.

If you are interested contact me. I need to make final travel plans in the next few weeks so please don’t wait. This is more or less first come, first served, but I will try to accommodate people as much as I can.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati

Posted in PowerShell, Training | Tagged , | Leave a comment

Export and Import Hash Tables

I use hash tables quite a bit and with the impending arrival of PowerShell 3.0 I expect even more so. PowerShell v3 allows you to define a hash table of default parameter values. I’m not going to to cover that feature specifically, but it made me realize I needed a better way to export a hash table, say to a CSV file. So I put together a few functions to do just that.

To walk you through them, here’s a simple hash table.

$hash=@{Name="jeff";pi=3.14;date=Get-Date;size=3 }
$hash
Name                           Value
----                           -----
Name                           jeff
pi                             3.14
date                           2/2/2012 10:04:54 AM
size                           3

I want to export this to a CSV file, but because PowerShell is all about the objects, I want to be sure to get the type information as well. Otherwise when I go to importing, everything will be a string. Here’s what I can expect to export:

$hash.GetEnumerator() |  Select Key,Value,@{Name="Type";Expression={$_.value.gettype().name}}

Key                        Value                      Type
---                        -----                      ----
Name                       jeff                       String
pi                         3.14                       Double
date                       2/2/2012 10:05:57 AM       DateTime
size                       3                          Int32

That looks good. I can take this command and run it through Export-CSV which gives me this file:

#TYPE Selected.System.Collections.DictionaryEntry
"Key","Value","Type"
"Name","jeff","String"
"pi","3.14","Double"
"date","2/2/2012 10:05:57 AM","DateTime"
"size","3","Int32"

Perfect. Later, I will need to import this file and recreate my hash table. I can use Import-CSV as a starting point.

PS C:\> import-csv hash.csv

Key                        Value                      Type
---                        -----                      ----
Name                       jeff                       String
pi                         3.14                       Double
date                       2/2/2012 10:05:57 AM       DateTime
size                       3                          Int32

Good so far. All I need to do is create a hash table and add each entry to it. I could do something like this:

Import-csv hash.csv | foreach -begin {$hash=@{}} -process {$hash.Add($_.Key,$_.Value)} -end {$hash}

But if I do this, everything will be a string. Since I have Type information, let’s use it.

Import-Csv -Path $path | ForEach-Object -begin {
     #define an empty hash table
     $hash=@{}
    } -process {
       <#
       if there is a type column, then add the entry as that type
       otherwise we'll treat it as a string
       #>

       if ($_.Type) {
         
         $type=[type]"$($_.type)"
       }
       else {
         $type=[type]"string"
       }
       Write-Verbose "Adding $($_.key)"
       Write-Verbose "Setting type to $type"
       
       $hash.Add($_.Key,($($_.Value) -as $type))

    } -end {
      #write hash to the pipeline
      Write-Output $hash
    }

Here I’m taking the Type value from the import and turning it into a System.Type object which I can then use to cast each value to the correct type. I’m checking for the Type property because I might have a CSV file without it. But as long as I have column headings for Key and Value this will work.

I turned all of this into a pair of advanced functions, Export-HashtoCSV and Import-CSVtoHash.

PS C:\> $hash | Export-HashtoCSV myhash.csv
PS C:\> $newhash=Import-CSVtoHash .\myhash.csv -verbose
VERBOSE: Importing data from .\myhash.csv
VERBOSE: Adding Name
VERBOSE: Setting type to string
VERBOSE: Adding pi
VERBOSE: Setting type to double
VERBOSE: Adding date
VERBOSE: Setting type to System.DateTime
VERBOSE: Adding size
VERBOSE: Setting type to int
VERBOSE: Import complete
PS C:\> $newhash

Name                           Value
----                           -----
Name                           jeff
pi                             3.14
date                           2/2/2012 10:05:57 AM
size                           3
PS C:\> $newhash.date

Thursday, February 02, 2012 10:05:57 AM
PS C:\> $newhash.pi.gettype().name
Double

This certainly fulfills my needs. You can download a script file with both functions, including help. As always, enjoy and I hope you’ll let me know how these work out for you.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati

Posted in PowerShell, PowerShell v2.0, Scripting | Tagged , , , , , , | 13 Comments

PowerShell 3.0 Easy Rider

Today I gave an Live Meeting presentation for the PowerShell Virtual User Group. When the recording is posted I’ll update this post. I spoke about new features in Windows PowerShell 3.0 that I think will make it easier for people to use PowerShell more efficiently. I think they even might find it fun. Here is what I covered.

 

 

AGENDA
 New Help Options
 Default Parameter Values
 Simplified Syntax
 Improved Tab Completion
 Redirection Options
 Improved ISE Experience

The presentation started with some introductory slides, but most of the presentation was demo. As promised here is my presentation and a zip files with my demos and sample script.

Here is the link to the recording

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati

Posted in Powershell 3.0, Professional, Training | Tagged , , , | Leave a comment

Maximizing the PowerShell Console Title Bar

A few days ago Boe Prox posted some very nifty PowerShell modules for using the title bar as a ticker for RSS feeds like the weather. I thought this was an awesome idea and an easy way to take advantage of what would otherwise be unused screen space. I was especially intrigued with his use of timer objects and event subscriptions to manage the updating.

Naturally I decided to run with this. My main goal was to take Boe’s fundamental idea and turn it into something more re-usable or extensible. My result is a module called ConsoleTitle.

PS C:\> get-command -Module ConsoleTitle | Select Name

Name
----
Get-Inspiration
Get-SystemStat
Get-Timer
New-Timer
Remove-Timer
Set-ConsoleTitle
Set-TimerInterval
Start-TitleTimer

The overall premise is pretty simple, define a global variable $PSConsoleTitle and use a timer to periodically update the console title bar with this value. During the refresh interval you can run whatever code you like, however you like, to provide a new value to the variable. In the module I’ve included two sample commands, Get-SystemStat and Get-Inspiration. The former uses WMI to gather system information from the local computer.

The other command defines an array of slogans, sayings and suggestions and randomly selects one to use as the title bar text.

The module includes a few commands for working with timer objects. You can use New-Timer in your own scripts. Here’s the function.

Function New-Timer {

<#
.Synopsis
Create an event timer object
.Description
Create an event timer object, primarily to be used by the ConsoleTitle module.
Each timer job will automatically be added to the global variable, $ConsoleTitleEvents
unless you use the -NoAdd parameter. This variable is used by Remove-Timer to clear
console title related timers.

This function is called from within other module functions but you can use it to
create non-module timers.

.Parameter Identifier
A source identifier for your timer
.Parameter Refresh
The timer interval in Seconds. The default is 300 (5 minutes). Minimum
value is 5 seconds.
.Parameter Action
The scriptblock to execute when the timer runs down.
.Parameter NoAdd
Don't add the timer object to the $ConsoleTitleEvents global variable.
#>

Param(
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a source identifier for your timer")]
[ValidateNotNullorEmpty()]
[string]$Identifier,
[Parameter(Position=1)]
[validatescript({$_ -ge 5})]
[int]$Refresh=300,
[Parameter(Position=2,Mandatory=$True,HelpMessage="Enter an action scriptblock")]
[scriptblock]$Action,
[switch]$NoAdd
)

Write-Verbose ("Creating a timer called {0} to refresh every {1} seconds." -f $Identifier,$Refresh)

#create a timer object
$timer = new-object timers.timer
#timer interval is in milliseconds
$timer.Interval = $Refresh*1000
$timer.Enabled=$True

#create the event subscription and add to the global variable
$evt=Register-ObjectEvent -InputObject $timer -EventName elapsed –SourceIdentifier  $Identifier  -Action $Action

if (-Not $NoAdd) {
    #add the event to a global variable to track all events
    $global:ConsoleTitleEvents+=$evt
}
#start the timer    
$timer.Start()

} #Function

And here’s how you might use it.

Function Get-Inspiration {

Param(
[Parameter(Position=0)]
[ValidateScript({$_ -ge 5})]
[int]$Refresh=600
)

#Define an array of pithy sayings, slogans and quotes

#we'll create as a globally scoped variable so you can add to it anytime you want from PowerShell
$global:slogans=@(
"PowerShell Rocks!",
"Energize!!",
"To Shell and Back",
"I am the Shell",
"PowerShell to the People",
"Powered by PS",
"PowerShell Rulez!",
"PowerShell Fanboy",
"I am the walrus",
"Those who forget to script are doomed to repeat their work.",
"Have you backed up files lately?",
"Is your resume up to date?",
"Is it Beer O'Clock yet?",
"With great power comes great responsibility",
"I came, I saw, I scripted.",
"$env:username, Open the pod bay doors."
)

$sb={ $global:PSConsoleTitle=$global:slogans | get-random }
#invoke the scriptblock
Invoke-Command $sb

New-Timer -identifier "SloganUpdate" -action $sb -refresh $refresh

#start the update timer if not already running
if (-Not (Get-EventSubscriber -SourceIdentifier "TitleTimer" -ea "SilentlyContinue")) {
    Start-TitleTimer -refresh $refresh
}

} #function

Think of the module as a framework or SDK for building your own solutions. The module also includes an about topic. I hope you’ll download ConsoleTitleand let me know what you think.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati

Posted in PowerShell v2.0, Scripting, WMI | Tagged , , , , , | 2 Comments

Friday Fun Get Content Words

Recently I was tracking down a bug in script for a client. The problem turned out to be a simple typo. I could have easily avoided that by using Set-StrictMode, which I do now, but that’s not what this is about. What I realized I wanted was a way to look at all the for “words” in a script. If I could look at them sorted, then typos would jump out. At least in theory.

My plan was to get the content of a text file or script, use a regular expression pattern to identify all the “words” and then get a sorted and unique list. Here’s what I came up with.

Function Get-ContentWords {

[cmdletbinding()]

Param (
[Parameter(Position=0,Mandatory=$True,
HelpMessage="Enter the filename for your text file",
ValueFromPipeline=$True)]
[string]$Path
)

Begin {
    Set-StrictMode -Version 2.0
   
    Write-Verbose "Starting $($myinvocation.mycommand)"
   
    #define a regular expression pattern to detect "words"
    [regex]$word="\b\S+\b"
}

Process {

    if ($path.gettype().Name -eq "FileInfo") {
      #$Path is a file object
      Write-Verbose "Getting content from $($Path.Fullname)"
      $content=Get-Content -Path $path.Fullname
    }
    else {
        #$Path is a string
        Write-Verbose "Getting content from $path"
        $content=get-content -Path $Path
    }

    #add a little information
    $stats=$content | Measure-Object -Word
    Write-Verbose "Found approximately $($stats.words) words"

    #write sorted unique values
    $word.Matches($content) | select Value -unique | sort Value
 }
 
End {
    Write-Verbose "Ending $($myinvocation.mycommand)"
    }
   
} #close function

The function uses Get-Content to retrieve the content (what else?!) of the specified file. At the beginning of the function I defined a regular expression object to find “words”.

#define a regular expression pattern to detect "words"
[regex]$word="\b\S+\b"

This is an intentionally broad pattern that searches for anything not a space. The \b element indicates a word boundary. Because this is a REGEX object, I can do a bit more than using a basic -match operator. Instead I’ll use the Matches() method which will return a collection of match objects. I can pipe these to Select-Object retrieving just the Value property. I also use the -Unique parameter to filter out duplicates. Finally the values are sorted.

$word.Matches($content) | select Value -unique | sort Value

The matches and filtering are NOT case-sensitive, which is fine for me. With the list I can see where I might have used write-host instead of Write-Host and go back to clean up my code. Let me show you how this works. Here’s a demo script.

#Requires -version 2.0

$comp = Read-Host "Enter a computer name"

write-host "Querying services on $comp" -fore Cyan
$svc = get-service -comp $comp

$msg = "I found {0} services on $comp" -f $svc.count
Write-Host "Results" -fore Green
Write-Host $mgs -fore Green

The script has some case inconsistencies as well as a typo. I’ve dot sourced the function in my PowerShell session. Here’s what I end up with.

For best results, you need to make sure there are spaces around commands that use the = sign. But now I can scan through the list and pick out potential problems. Sure, Set-StrictMode would help with variable typos but if I had errors in say comment based help, that wouldn’t help. Maybe you’ll find this useful in your scripting work, maybe not. But I hope you learned a few things about working with REGEX objects and unique properties.

Download Get-ContentWords and enjoy.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati

Posted in Friday Fun, PowerShell v2.0, Scripting | Tagged , , , , , | Leave a comment