My Second Appearance on the PowerScripting Podcast

I had the great pleasure of being on the PowerScripting Podcast last night to talk WSUS, PowerShell and anything else that happened to come up during my time there. As always, it was great honor to be a guest on the show with Hal and John and I definitely had a blast on there! This was also my second appearance on the show (the last show I talked PoshPAIG).

The podcast hasn’t been published yet, but I will make an update when it is published with the link to the show.

http://powerscripting.wordpress.com/2013/04/09/up-next-microsoft-community-contributor-boe-prox-talks-about-wsus-and-more/

[Update 18 April 2013]The interview was just posted last night. So here it is:

http://powerscripting.wordpress.com/2013/04/17/episode-224-boe-prox-talks-about-poshwsus-and-his-other-projects/

Posted in News | Tagged , , , | 1 Comment

PoshWSUS Updated to 2.1.1

I just released a new version of my WSUS module, PoshWSUS to codeplex. Nothing new was added to this release, so only bug fixes that were submitted to the Issues page on the codeplex site were addressed.

Let me know what you think and don’t forget to log any bugs to the Issues page!

PoshWSUS Site:

https://poshwsus.codeplex.com/

Release Notes:

https://poshwsus.codeplex.com/releases/view/104819

Issues Page:

https://poshwsus.codeplex.com/workitem/list/basic

Posted in powershell, WSUS | Tagged , , , | Leave a comment

Creating Snippets in PowerShell

For those of you using PowerShell V3’s new Integrated Script Editor (ISE) and be honest, why aren’t you? Besides all of the awesome stuff with code folding, intellisense, etc…, there is also another cool feature that is available is the ability to create and use snippets in the ISE.

An example is the already available snippets that are available in the ISE. All you have to do is click CTRL+J (you can also right click and select Start Snippets if that is your thing) and the snippets context menu opens up with a nice selection of snippets to choose from.

image

You can scroll through all of the available snippets and each one has a tooltip which allows you to see the code as well as a description/author of the snippet. Once you select the snippet, it will be pasted into the current tab on the ISE session. Given, all of these snippets are just templates that you can use to more easily create an advanced function, new workflow or just add some flow control to an existing script or function.

So what if you have your own set of snippets in a file or something that you wanted to create on the fly to have available later? Luckily, the PowerShell team put together a small module called ISE in PowerShell V3 that makes it very easy to create and add your own snippets into the ISE which are just PS1XML files. You can even use (to an extent) the module in a PowerShell console.

The three cmdlets available in the ISE module are:

  • Get-IseSnippet
  • Import-IseSnippet
  • New-IseSnippet

Scripts created by users are created and saved in your $home\WindowsPowerShell\Snippets folder. Because I do not have a “user made snippet”, when I run Get-IseSnippet, nothing happens.

Get-IseSnippet

image

Creating Your First Snippet

Using the ISE, I type the following code to run that will create the snippet and store it in the Snippets folder mentioned earlier.

New-IseSnippet -Title "Test Snippet" -Author "Boe Prox" -Description "Just a test snippet" -Text "
Function Verb-Noun {
    [cmdletbinding()]
    Param (
        [parameter()]
        [string[]]$Object
    )
    Begin {}
    Process {}
    End {}
}
"

Now lets run the Get-IseSnippet again and see what happens.

image

And there is the new snippet ready to go. Because I did this in the ISE, it was already loaded into the current session and available to use!

image

The mandatory parameters to use when creating a snippet are the Description, Title and Text with the Text being the actual code snippet. This must be a string and not an array as it will throw an error if using anything other than a string.

Remember when I said you could create snippet from the console? Well, the only catch to this is that because you are not running it from the ISE, it will throw an error stating that it couldn’t load the snippet into the ISE (because it doesn’t exist in a console, of course!). The file is still created and will be loaded into the ISE when you start it up or if you use the Import-IseSnippet cmdlet.

image

By the way, it is just a file, so you can use Remove-Item to easily remove that snippet file. Or if you wanted to overwrite it, simply use the –Force parameter as show in my example.

If you have a file that you want to make a snippet, it is simple to make it into a snippet.

New-IseSnippet -Title "Test Snippet" -Author "Boe Prox" `
-Description "Just a test snippet" `
-Text (Get-Content .\SomeCodeSnippet.ps1)

This should work, right? Nope! This will fail because when you use Get-Content like I shown, it is actually will not work because the data returned is an object, not a string that it requires.

image

get-member -input (Get-Content .\SomeCodeSnippet.ps1)

image

You will need to use the –Raw parameter in Get-Content to make it work correctly.

get-member -input (Get-Content .\SomeCodeSnippet.ps1 -Raw)

image

This is the correct code to use to create a new snippet from a file.

New-IseSnippet -Title "Test Snippet" -Author "Boe Prox" `
-Description "Just a test snippet" `
-Text (Get-Content .\SomeCodeSnippet.ps1 -Raw)

You could also pipe the the output of Get-Content to Out-String as well to get the same results.

Lastly, let’s assume that there is a repository of snippets out on some server that you want to be able to add into your ISE snippets library. To do this, we would use Import-IseSnippet.

Import-IseSnippet -Path "\\dc1\SharedStorage\snippets" -Recurse

I just decided to grab all of the snippets that were in that folder to import into my current ISE session. Because I imported the snippets, they are not added to my personal snippets folder and once you close out of the ISE session, the snippets must be re-imported back into the ISE session.

Hopefully this will get you started to create your own PowerShell snippets using what I have already shown you! I would love to see what snippets you have created and what you are using them for. Available for download below is a snippet I created that I use frequently for creating background runspaces on various types of queries I use for functions. Feel free to download it and let me know your thoughts!

PowerShell Runspace Snippet

image

Posted in powershell, V3 | Tagged , , | 4 Comments

Split-Path Performance in PowerShell

While working on my previous article, I was trying to determine the best way to lay out all of the properties I wanted. While I was getting the full path of a file, I also wanted just the filename as well in my output to make it easier to read when displayed in the console. At first I thought the obvious answer was to use Split-Path with the –Leaf parameter to get the filename. But in testing I found that this caused a performance hit on my function. Given the performance difference is measured in milliseconds, it still stands as something I would like to fine tune to make it run faster.

Using the following example as the baseline, I am going to run a test against 1000 filenames 50 times and take the average time in milliseconds. Starting out with my Split-Path approach first, you will see the time in milliseconds that it took on average to complete.

Split-Path Example

1..50 | ForEach {
    (Measure-Command {
        1..1000 | ForEach {
            $File = "C:\users\Administrator\Desktop\100.jpg" 
            Split-Path -Path $File -Leaf
        }
    }).TotalMilliseconds
} | Measure-Object -Average

Count    : 50
Average  : 340.56
Sum      :
Maximum  :
Minimum  :
Property :

Substring Example

I looked at using the Substring() method which is available in a string object to split off the filename from the full path. To do this, I had to also make use of the LastIndexOf() method and supply the final backslash “\” to help in getting only the filename. The example below shows how this turned out.

1..50 | ForEach {
    (Measure-Command {
        1..1000 | ForEach {
            $File = "C:\users\Administrator\Desktop\100.jpg" 
            $File.Substring($File.LastIndexOf("\")+1)
        }
    }).TotalMilliseconds
} | Measure-Object -Average

Count    : 50
Average  : 54.897506
Sum      :
Maximum  :
Minimum  :
Property :

A pretty significant improvement in speed. Obviously, as you add more files into the equation, the amount of time will increase. I’ll demonstrate that later along with some other interesting things that I found.

But that is not all folks! There is yet another way to split off the filename from a path. This time it comes from the GetFileName() method from the IO.Path type. Lets give that a shot using the same process as before to see what happens.

1..50 | ForEach {
    (Measure-Command {
        1..1000 | ForEach {
            $File = "C:\users\Administrator\Desktop\100.jpg" 
            [io.path]::GetFileName($file)
        }
    }).TotalMilliseconds
} | Measure-Object -Average

Count    : 50
Average  : 51.22724
Sum      :
Maximum  :
Minimum  :
Property :

This is the fastest method yet to grab the filename from a path.  Almost 300 milliseconds faster than Split-Path and a little faster than the SubString() method.

Because I had some free time, I wrote a little script to really get an idea on the differences in speed between these 3 methods. The script name is SplitPathPerfDemo.ps1 and the source code is available at the end of the article.

This script has a couple of parameters:

  • MaxFiles
    • Simulates any number of files to perform the path splitting for a file name
  • Iterations
    • The number of times to perform this measurement against splitting the filename from a given path.
  • FilePath
    • The full path to a file.

The basics of the script are that it performs n iterations against n files and gets an average time in milliseconds for each type of split and displays the output on the screen. I can also specify a specific path (it doesn’t have to exists) so you can see the depth of the path as well.

First lets run it with the default values (100 iterations, 100 files, 46 characters in path).

\SplitPathPerfDemo.ps1 | 
    Sort Time_ms -Descending | 
        Format-Table

image

Just as we saw before, the slowest was Split-Path and the fastest was using the GetFileName() method of IO.Path.

Now lets increase the number of files to 1000 and see the results.

\SplitPathPerfDemo.ps1 -MaxFiles 1000 | 
    Sort Time_ms -Descending | 
        Format-Table

image

Again, Split-Path was the slowest with the Substring() method being the fastest this time.

Something that I wanted to see was what happens when the path depth increases. Would the time increase as well for each of the methods? Leaving the Iterations and MaxFiles at their default value of 100.  The character depth will be 196 characters.

.\SplitPathPerfDemo.ps1 -Maxfiles 1000 -FilePath "C:\Users\Administrator\Desktop\PowerShellScripts\Thisis
alonglongnamedfolderformetouse\AnotherThisisalonglongnamedfolderformetouse\YetAnotherThisisalonglongnamedfolderformetouse\Get-Constructor.ps
1" | Sort Time_ms -Descending | Format-Table

image

Compare that to the same defaults of the 46 character path.

image

Now the more interesting side of this. The actual time difference is more due to the backslashes “\” in the path than the actual number of characters in the path. The following example is with only 2 backslashes.

.\SplitPathPerfDemo.ps1 -Maxfiles 1000 -FilePath "C:\Users123456AdministratorDesktopPowerShellScriptsThisisalonglongnamedfolderformetouseAnotherThisisalonglongnamedfolderformetouseYetAnotherThisisalonglongnamedfolderformetouse\Get-Constructor.ps1" | Sort Time_ms -Descending | Format-Table

image

Now for one with more backslashes in it.

.\SplitPathPerfDemo.ps1 -Maxfiles 1000 -FilePath "C:\a\b\c\d\e\f\g\h\j\k\l\l\g\h\y\r\e\d\Get-Constructor.ps1" | Sort Time_ms -Descending | Format-Table

image

Only 46 characters in the file, but with all of those backslashes, it is almost as time consuming as the splitting of a path with 196 characters! With that it seems like the more backslashes that one has in a path, the greater the impact to the performance of splitting a path.

So with that, it would see that using IO.Path with the GetFileName() method to quickly get the filename from a path. Of course, if using Get-ChildItem, then you have no need for this method but for those special cases when you would need to perform multiple split operations, this might be something to keep in mind.

Source Code for SplitPathPerfDemo.ps1

[cmdletbinding()]
Param (
    [int]$MaxFiles = 100,
    [int]$Iterations = 100,
    [string]$FilePath = "C:\users\Administrator\Desktop\users\photo.jpg"
)
1..$Iterations | ForEach {
    (Measure-Command {
        1..$MaxFiles | ForEach {
            Split-Path -Path $FilePath -Leaf
        }
    }) | Select TotalMilliseconds, TotalSeconds
} | Measure-Object -Average -Property TotalMilliseconds | ForEach {
    New-Object PSObject -Property @{
        Test = 'Split-Path'
        Iterations = $Iterations
        FilesCount = $MaxFiles
        Time_ms = [math]::Round($_.Average,2)
        PathDepth = $FilePath.length
    }
}

1..$Iterations | ForEach {
    (Measure-Command {
        1..$MaxFiles | ForEach {
            $FilePath.Substring($FilePath.LastIndexOf("\")+1)
        }
    }) | Select TotalMilliseconds, TotalSeconds
} | Measure-Object -Average -Property TotalMilliseconds | ForEach {
    New-Object PSObject -Property @{
        Test = 'Substring()'
        Iterations = $Iterations
        FilesCount = $MaxFiles
        Time_ms = [math]::Round($_.Average,2)
        PathDepth = $FilePath.length
    }
}

1..$Iterations | ForEach {
    (Measure-Command {
        1..$MaxFiles | ForEach {
            [io.path]::GetFileName($FilePath)
        }
    }) | Select TotalMilliseconds, TotalSeconds
} | Measure-Object -Average -Property TotalMilliseconds | ForEach {
    New-Object PSObject -Property @{
        Test = 'io.path'
        Iterations = $Iterations
        FilesCount = $MaxFiles
        Time_ms = [math]::Round($_.Average,2)
        PathDepth = $FilePath.length
    }
}
Posted in powershell | Tagged , , | 3 Comments

Excerpt of WSUS Chapter from PowerShell Deep Dives on Hey, Scripting Guy!

An excerpt from one of my chapters on WSUS from the PowerShell Deep Dives book was posted on Hey, Scripting Guy! today. Check it out and let me know what you think!

http://blogs.technet.com/b/heyscriptingguy/archive/2013/04/02/windows-software-update-services.aspx

Posted in powershell | Tagged , , | Leave a comment