Summary: Learn how to use Windows PowerShell to download code snippets from the Internet and install them for use in the Windows PowerShell ISE.

 

Microsoft Scripting Guy Ed Wilson here. It seems as if there is an old song that rather describes things right now. It goes somewhat like this:

The weather outside is frightful,

But the code inside is delightful,

Since there is no place to go,

Let us code, let us code let us code.

Oh, my PowerShell code is so sweet,

And the syntax is so neat,

Since there is no place to go,

Let us code, let us code let us code.

Or words to that effect. It seems that many of my friends on Facebook are complaining that they are getting snowed out. Even some of my mates at the university are getting into the act—in an ambivalent sort of way. They really love their classes, but they would not mind a day off. Of course, a day in which one is stuck in the dormitory is not much of a day off.

The Scripting Wife and I decided to evacuate from Charlotte and the possibility of inclement weather, and we headed to the coast. The photo shown here is one I took on the beach.

Photo of non-Hitchcockian seagulls

The nice thing about sitting on the balcony, listening to the seagulls, and watching the waves as they roll in and out is that it is a great place to write Windows PowerShell code. Last weekend’s blog posts about code snippets generated several comments via the comment button and in email. The long and the short of the comments was that browsing to a folder and copying the content of a script is not a code snippet. And that, of course, is correct.

To be truly useful, a code snippet needs to be pretty much set up as a code snippet. In Visual Studio, there is actually a feature called code snippets, and to write your own code snippets, you need to monkey around with some XML, which means that there is an actual code snippet schema. Well, that is all fine and dandy, but it is a bit more than I want to do on a Saturday morning. (PowerGUI has actual snippets with an XML schema and everything.)

What I want to do is to provide an easy way to use the concept of code snippets without the heavy lifting of XML or the requirement to use third party software. So, let’s review where I am a in my code snippet project. First, I talked about using code snippets, and I wrote code that displays an open file dialog box and allows me to select a file. After I select the file, the code reads the content of the file and inserts it at the current insertion point of my script. On Sunday, I wrote a script that creates a custom .snip file extension and sets it up in the registry, associated with Notepad. This was the second step in the process.

Today I need to create a bunch of .snip files that will become my code snippet collection. I will put them all in a snip folder. The .snip files will be my code snippets. In addition, if I have special functions that I like to reuse, there is no need to translate them into .snip files, so I will add the .snip file extension to the filter for my open file browser. The nice thing about the .snip files being simple plain text files is that anyone can create their own code snippet by adding the file to the directory that contains the snippets. I thought about adding a snippet directory off the root, but I think it might actually be a better practice to add the snippet directory to my user profile instead. I figured I would create a snip folder in the same location that my Windows PowerShell profile resides. To find that location, I use the $profile automatic variable, and pass it to the Split-Path cmdlet. This appears here along with the results of the command.

PS C:\> Split-Path $profile
C:\Users\edwils\Documents\WindowsPowerShell

I created 11 .SNIP files. The files are available in a zipped file. The snip files do not include any comments and in most cases are bare shells of code. The code snippets save typing. In a few cases, I used generic variables such as $x and $y, as shown here.

if($x -gt $y)
{ }

Nevertheless, sometimes I did not include any generic variables because I did not want to clutter up the snippet. An example of this type of snippet is shown here:

Do
{
} Until ( )

The cool thing is this: The files are plain text, and if you do not like the way I formatted the code or failed to include comments, go ahead and change them. In fact, after you have added to the collection of snippets, feel free to upload them to the Scripting Guys Script Repository.

I wrote a really cool script that downloads the snippet.zip file from the Scripting Guys SkyDrive, creates a snippet folder under the Windows PowerShell folder in my personal profile, and then unzips the .SNIP files into the snippet folder. The Install-CodeSnippetsFromInternet.ps1 script is shown here.

Install-CodeSnippetsFromInternet.ps1

$source = "http://bit.ly/ga0k0i"

$folder = New-Item -Path (Split-Path -Path $profile -Parent) `

-Name snippet -ItemType directory

$destination = Join-Path -Path $folder -ChildPath snip.zip

$wc = New-Object system.net.webclient

$wc.downloadFile($source,$destination)

$shell = New-Object -ComObject shell.application

$files = $shell.namespace($destination).items()

$shell.NameSpace($folder.fullname).copyHere($files)

The actual destination for the $source file, which is the snippet.zip file, is really long. It is

$source = http://public.bay.livefilestore.com/y1pWfzVoKQeFsgOgBR6btSYG7ToGKfuivGSfP0z5eiDQ_erRBwRw9hZpd7Nue9wD_RSL6JQ9Jg2PlWqLSgMlrr0vw/snip.zip

but I think the code looks better with the shortened URL. The bit.ly URL works in my testing, but I know there were some problems with bit.ly recently, and therefore it is a potential source of disruption. In my actual script, I have the long URL in a commented out block, just in case. Discovering the actual URL to use for the download was rather cumbersome. I ended up using Fiddler and looking through the traces to find the actual path to the snip.zip file (but then I wrote a book on Network Monitoring and Analysis, so network traces do not bother me very much).

After I discovered the path to the snip.zip file that I want to download, using the System.Net.WebClient is pretty easy. After the webclient object is created, I call the downloadfile method and pass the source and the destination to the method. One thing to keep in mind is that the destination must point all the way to a file name:

$wc = New-Object system.net.webclient

$wc.downloadFile($source,$destination)

To unzip the files from the snip.zip file, I use the shell.application COM object. This technique is shown here:

$shell = New-Object -ComObject shell.application

$files = $shell.namespace($destination).items()

$shell.NameSpace($folder.fullname).copyHere($files)

Cool. Now that I have a bunch of .snip files, I will modify the snippet explorer that I created last week. Because I am making a couple of changes to the script, I modify the name. I am also adding a line to the bottom of the script that calls the function while passing the initial snippet directory to it. The revised Get-CodeSnippetV2.ps1 script appears here.

Get-CodeSnippetV2.ps1

Function Get-CodeSnippetV2

{

<#

.Synopsis

Either displays contents of a file in Notepad, or adds the content to

current insertion point of script.

.Description

The Get-CodeSnippetV2 function accepts an input folder of code snippets,

and displays a file dialog box that allows you to select a particular

file to either view the contents in Notepad, or add the content to

your script at the current insertion point.

.Example

Get-CodeSnippetV2 -InitialDirectory c:\fso

Opens a File dialog box situated on the c:\fso directory. Initial view

is .ps1 and .psm1 files, but you can choose to view all files. If you

select a file, its contents will be displayed in Notepad.

.Example

Get-CodeSnippetV2 -InitialDirectory c:\fso -add

Opens a File dialog box situated on the c:\fso directory. Initial view

is .ps1 and .psm1 files, but you can choose to view all files. If you

select a file, its contents will automatically be added at the current

insertion point of your script.

.Parameter InitialDirectory

The initial directory to be homed for the Open File dialog box

.Parameter Add

A switched parameter that will cause the function to add the content to

current script at current insertion point.

.Inputs

[string]

.Outputs

[string]

.Notes

NAME: Get-CodeSnippetV2

AUTHOR: Ed Wilson, Microsoft

LASTEDIT: 02/10/2011 13:14:07

KEYWORDS: Weekend Scripter, scripting techniques, scripting templates

HSG: WES-2-12-2011

.Link

Http://www.ScriptingGuys.com

#Requires -Version 2.0

#>

Param(

[Parameter(Mandatory=$true)]

[string]$InitialDirectory,

[switch]$add

)

If($ExecutionContext.Host.name -notmatch "ISE Host$")

{

$response = Read-Host -Prompt "This script must run in Windows PowerShell ISE. <Y> to exit."

if($response -match "y") { Exit }

} #end if

Add-Type -AssemblyName "System.windows.forms"

$OpenFileDialog = new-object System.Windows.Forms.OpenFileDialog

$OpenFileDialog.initialDirectory = $initialDirectory

$OpenFileDialog.filter = "PowerShell Scripts (*.ps1;*.psm1;*.snip)|*.ps1;*.psm1;*.snip|All files (*.*)| *.*"

$OpenFileDialog.ShowDialog() | out-null

Try

{

if(!$add)

{ notepad $openfileDialog.filename }

if($add)

{

$content = Get-Content -Path $OpenFileDialog.filename -Encoding ascii -Delimiter `r`n

$psise.CurrentFile.Editor.InsertText($content)

} #end if $add

} #end try

Catch [System.Exception]

{ "No file was selected." }

} #end function Get-CodeSnippet

Get-CodeSnippetV2 -InitialDirectory (Join-path -path (Split-Path -path $profile -Parent) -ChildPath snippet)

 

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson, Microsoft Scripting Guy