PowerShell Cookbook

Search

Categories

 

On this page

Download the Official Windows PowerShell 1.0 Release!
A New Virtual Earth Mashup in Born
Mathematical Pumpkins
Writing PowerShell Cmdlets vs. Writing PowerShell Providers
Load a Custom DLL from PowerShell
Email Quoting and Wrapping in 59 Bytes
Get-HelpMatch - Search Help (Apropos) in PowerShell
Counting Lines of Source Code in PowerShell
Updated: PowerShell Profile Locations Rationale
Great PowerShell Screencasts

Archive

Blogroll

Disclaimer
I work for Microsoft.

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 216
This Year: 16
This Month: 0
This Week: 0
Comments: 523

Sign In

 Tuesday, November 14, 2006
Tuesday, November 14, 2006 10:37:33 AM (Pacific Standard Time, UTC-08:00) ( )

A few minutes ago, Bob Muglia and Jeffrey Snover at IT Forum announced something at we've long been waiting for – the official Release to Web (RTW) of Windows PowerShell v1.0!

In late December of 2002, PowerShell entered the public eye when a Slashdot reader stumbled upon a job posting for "Microsoft's Next Generation Shell". About a year later, we officially announced the project (code-named "Monad") at the 2003 Microsoft Professional Developer's Conference. Things started to really pick up steam when we released our first really public preview in September of 2004 – a fact not missed by the ever vigilant Slashdot.
Since then, we've shipped incremental previews frequently, gaining enormous benefit from all of you – a passionate and active community of devoted beta enthusiasts:

"Monad" Beta 1 - 06/17/2005
"Monad" Beta 2 - 09/13/2005
"Monad" Beta 3 - 01/10/2006
"Monad" Beta 3.1 - 03/09/2006
Windows PowerShell RC1 - 04/25/2006
Windows PowerShell RC2 - 09/27/2006
Windows PowerShell RTW – 11/14/2006

Each release added features, fixed bugs, and incorporated heaps of changes initiated by feedback from the community.

This has been a long and adventurous journey – something that's truly been worth relishing. Whether you are an old or new participant in the PowerShell community, you should feel proud of your contributions – and for a job well done.

You can find pointers to the official release at http://www.microsoft.com/powershell -- so go install it, and enjoy the revolution!

Comments [6] | | # 
 Thursday, November 09, 2006
Thursday, November 09, 2006 8:28:08 AM (Pacific Standard Time, UTC-08:00) ( )

About 8 months ago, I wrote a Virtual Earth mashup to display commute times to Microsoft in and around the Seattle area. It's been very successful, and solves a big problem for many people looking to buy a house (amongst other things.)


http://www.leeholmes.com/projects/commute

About 3 or 4 months after posting the mashup, the Virtual Earth team changed a bunch of the APIs, which pretty much broke every existing mashup – including mine. I patched it up a bit some time later, but couldn't spend the time to fully restore its functionality. For example, the zooming controls were broken, as were the panning controls.

Neither writing the mashup (nor living through the breaking changes) were terribly pleasant experiences. The Virtual Earth map control was simply too basic. With not much effort, you get a map that scrolls, but nothing else. No UI, no support for panning, zooming, or anything else.

Want to make it interactive and snazzy? In my case, that meant copying tons of Virtual Earth infrastructure scripts, html, images, and other random junk in order to make something presentable.

However, the end result was definitely exciting despite all of that.

I checked on the APIs tonight to get a gauge on how much work was in front of me to fix it completely again, and was stunned. The Virtual Earth team has done a truly amazing job at focusing on making the job of a "masher" as easy as possible. Their programming model has matured significantly, placing a much cleaner boundary between your application and the Virtual Earth implementation details. It feels like a real API now, as opposed to forcing you into an invasive, parasitic implementation.

One innovation I'm immensely impressed with is their new interactive SDK. It takes cues from the cookbook / task-based approach to learning. First, you click on a node to say, "I want to --> Use pushpins --> Add a custom pushpin." Then, the SDK shows you – through an interactive working view, example code, and reference page -- exactly how to do it.

In the end, I didn't patch my old code back into working condition. I reused my support code, but rewrote the UI and interface to Virtual Earth again from scratch. In just a few hours I was done:

  • My index page has gone from 11k to 3k
  • My custom Javascript has gone from 6k to 5k
  • My entire project has shrunk from 391kb to 71kb
  • That furrow in my brow is gone

Kudos to the Virtual Earth team. The mashup experience is definitely a first-class citizen, and it shows.

Comments [0] | | # 
 Thursday, November 02, 2006
Thursday, November 02, 2006 7:22:44 AM (Pacific Daylight Time, UTC-07:00) ( )

Well, last night was Halloween, where I got the chance to enjoy the eerie glow of our scary pumpkin.  Well, scary if you don't like fractals, that is.

Last year, we carved out a Sierpinski Triangle -- which surprisingly only took a toothpick or two to repair isolated triangles:

This year, I wanted to stay with the fractal theme. I didn't necessarily want to attempt the Julia or Mandelbrot sets, so instead did a Sierpinski Carpet (along with a wee bit of evil, of course:)

 

Not being one to cut 64 of the level-three squares by hand, a cordless drill came in extremely handy.

Unfortunately, when I'm out trick-or-treating, there's nobody around to give candy to the little monsters.  I leave a note above a bowl on a chair -- this year, I finally realized why my calligraphy pens include red in the set!

 

Comments [0] | | # 
 Tuesday, October 31, 2006
Wednesday, November 01, 2006 12:20:46 AM (Pacific Daylight Time, UTC-07:00) ( )

A question that sometimes comes up is, "When do I write a PowerShell provider instead of a cmdlet?" To explain that, it is useful to understand how PowerShell distills management concepts for administrators.

In the pre-PowerShell world, the command-line management experience is fairly fractured. You have a huge number of awkwardly named commands, with no way to quickly determine what they mean. Many of them perform nearly identical actions, but have very different names. The image above illustrates a chaotic mix of commands that work primarily on files and directories.

The first approach that PowerShell uses to distill management concepts is to name commands with a Verb-Noun (Action, Item) pattern.

To make it easier for an administrator to learn the actions available for a new noun (such as Service or Process,) we provide strong guidance that verbs come from a standard set. The image above shows how you might name the earlier commands, while paying attention to the standardized verbs.

Now, what happens if you want to make these cmdlets work on the Registry?  How would you name them in that situation? Some of them apply exactly (but operate on a RegistryKey rather than a File,) some of them may not apply (such as the content of a registry key,) and some of them definitely do not apply (such as the list of running processes.)

Notice that some of the nouns share a lot in common, and converge to a few basic concepts. From the examples given above, you can perform actions on items, content, and locations.

The first set is clearly unmanageable, and makes life difficult for administrators.

The second set provides the main benefit of cmdlets. Consistent naming, and discoverability. Administrators can say, "I have a <Noun>, so already have a pretty good idea of what I can do with it."

The third set provides the main benefits of providers. For the major noun sets that we know about (items, content, properties, and navigation,) we make it as easy as possible for you to implement all related commands for that set. When you do, Administrators can say, "This provider supports navigation and items, so I already have a pretty good idea of what commands work with it."

So, if you want to support any of the major noun sets, you should write a provider.

As PowerShell matures, so should its list of provider types. For example, the *-Process cmdlets and *-Service cmdlets are strikingly familiar, and suggest the need for a standard lifecycle set of cmdlets.  It should also be noted that you can implement any or all of the standard provider types. For any that you implement, you get all related cmdlets for free.

[Edit: Resized images]

Comments [4] | | # 
 Friday, October 27, 2006
Friday, October 27, 2006 8:20:50 PM (Pacific Daylight Time, UTC-07:00) ( )

One of the amazing features of PowerShell is its amazing reach.  You can interact with the file system, the registry, certificate stores, COM, WMI, XML, cmdlets, providers … the list seems to go on forever.

One important extension point is the ability to seamlessly interact with .NET DLLs. These are most commonly shipped in SDKs, and many people have made use of this.  For example:

When pre-built API DLLs don't suit your need, you have yet another option -- writing your own! If you are comfortable with any of the .NET languages, it is quite simple to compile a DLL and load it into your PowerShell session.

Take, for example, a simple math library.  It has a static Sum method, and an instance Product method:

namespace MyMathLib
{
  public class Methods
  {
    public Methods()
    {
    }

    public static int Sum(int a, int b)
    {
      return a + b;
    }

    public int Product(int a, int b)
    {
      return a * b;
    }
  }
}

Here is everything required to get that working in your PowerShell system:

[C:\temp]

PS:25 > notepad MyMathLib.cs

 

(…)

 

[C:\temp]

PS:26 > csc /target:library MyMathLib.cs

Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42

for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727

Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.

 

 

[C:\temp]

PS:27 > [Reflection.Assembly]::LoadFile("c:\temp\MyMathLib.dll")

 

GAC    Version        Location

---    -------        --------

False  v2.0.50727     c:\temp\MyMathLib.dll

 

 

 

[C:\temp]

PS:28 > [MyMathLib.Methods]::Sum(10, 2)

12

 

[C:\temp]

PS:29 > $mathInstance = new-object MyMathLib.Methods

Suggestion: An alias for New-Object is new

 

[C:\temp]

PS:30 > $mathInstance.Product(10, 2)

20

Comments [8] | | # 
 Wednesday, October 25, 2006
Wednesday, October 25, 2006 7:29:43 AM (Pacific Daylight Time, UTC-07:00) ( )

Here's a useful bit of line noise that I came up with when I wanted to put some quoted text into email.  Starting with unwrapped text in the clipboard:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

It generates:

[D:\Temp]
PS:30 > pclip | %{[Regex]::Split($_,'(.{0,60}(?:\s|$))')}|%{if($_){"> $_"}}
> Lorem ipsum dolor sit amet, consectetur adipisicing elit,
> sed do eiusmod tempor incididunt ut labore et dolore magna
> aliqua. Ut enim ad minim veniam, quis nostrud exercitation
> ullamco laboris nisi ut aliquip ex ea commodo consequat.
> Duis aute irure dolor in reprehenderit in voluptate velit
> esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
> occaecat cupidatat non proident, sunt in culpa qui officia
> deserunt mollit anim id est laborum.

(pclip is the clipboard paste utility available from http://unxutils.sourceforge.net/)

Have any densely useful snippets you'd like to share?

Comments [2] | | # 
 Saturday, October 21, 2006
Sunday, October 22, 2006 1:40:44 AM (Pacific Daylight Time, UTC-07:00) ( )

To give a glimpse into the writing process behind my upcoming "Windows PowerShell - The Definitive Guide" (O'Reilly,) I'll occasionally post entries "as the author sees it."  The first in this series shows how to search the PowerShell help content for a phrase or pattern.

 

Program: Get-HelpMatch¶

Both the Get-Command and Get-Help cmdlets allow you to search for command names that match a given pattern. However, when you don't know exactly what you are looking for, you will more often have success searching through the help content for an answer. On Unix systems, this command is called Apropos.  Similar functionality does not exist as part of the PowerShell's help facilities, however.¶

That doesn't need to stop us, though, as we can write the functionality ourselves.¶

To run this program, supply a search string to the Get-HelpMatch script. The search string can be either simple text, or a regular expression. The script then displays the name and synopsis of all help topics that match. To see the help content for that topic, use the Get-Help cmdlet.¶

Example 1-4. Get-HelpMatch.ps1¶

##############################################################################¶

## Get-HelpMatch.ps1¶

##¶

## Search the PowerShell help documentation for a given keyword or regular¶

## expression.¶

## ¶

## Example:¶

##    Get-HelpMatch hashtable¶

##    Get-HelpMatch "(datetime|ticks)"¶

##############################################################################¶

 ¶

param($searchWord = $(throw "Please specify content to search for"))¶

 ¶

$helpNames = $(get-help *)¶

 ¶

foreach($helpTopic in $helpNames)¶

   $content = get-help -Full $helpTopic.Name | out-string¶

   if($content -match $searchWord)¶

  

      $helpTopic | select Name,Synopsis¶

  

 

Comments [1] | | # 
 Wednesday, October 18, 2006
Wednesday, October 18, 2006 8:49:13 PM (Pacific Daylight Time, UTC-07:00) ( )

Oren Eini recently ran into some performance problems while using PowerShell to count the number of lines in a source tree:

I wanted to know how many lines of code NHibernate has, so I run the following PowerShell command...
(gci -Recurse | select-string . ).Count
The result:
(Graphic of PowerShell still working after about 5 minutes, using 50% of his CPU.)
Bummer.

The performance problem from this command comes from us preparing for a rich pipeline experience in PowerShell that you never use.  With only a little more text, you could have run even more powerful reports:

Line count per path:
   gci . *.cs -Recurse | select-string . | Group Path
Min / Max / Averages:
   gci . *.cs -Recurse | select-string . | Group Filename | Measure-Object Count -Min -Max -Average
Comment ratio: 
   $items = gci . *.cs -rec; ($items | select-string "//").Count / ($items | select-string .).Count

But if you don't need that power, there are alternatives that perform better.  Let's look at some of them.  We'll use a baseline of the command that Oren started with:

[C:\temp]
PS:14 > $baseline = Measure-Command { (gci . *.cs -Recurse | Select-String .).Count }

… and a comparison to the LineCount.exe he pointed to:

PS:15> $lineCountExe = Measure-Command { C:\temp\linecount.exe *.cs /s }
PS:16 > $baseline.TotalMilliseconds / $lineCountExe.TotalMilliseconds
41.5567286307833

(The Select-String approach is about 41.5x slower)

Since we don't need all of the PowerShell metadata generated by Select-String, and we don' t need the Regular Expression matching power of Select-String, we can instead use the [File]::ReadAllText() method from the .NET Framework:

PS:17 > $readAllText = Measure-Command { gci . *.cs -rec | % { [System.IO.File]::ReadAllText($_.FullName) } | Measure-Object -Line }
PS:18 > $readAllText.TotalMilliseconds / $lineCountExe.TotalMilliseconds
3.30927987204783

This is now about 3.3x slower – but is only 87 characters!  With a PowerShell one-liner, you were able to implement an entire linecount program.
If you want to go further, you can write a linecount program yourself:

## Get-LineCount.ps1
## Count the number of lines in all C# files in (and below)
## the current directory.

function CountLines($directory)
{
    $pattern = "*.cs"
    $directories = [System.IO.Directory]::GetDirectories($directory)
    $files = [System.IO.Directory]::GetFiles($directory, $pattern)

    $lineCount = 0

    foreach($file in $files)
    {
        $lineCount += [System.IO.File]::ReadAllText($file).Split("`n").Count
    }

    foreach($subdirectory in $directories)
    {
        $lineCount += CountLines $subdirectory
    }

    $lineCount
}

CountLines (Get-Location)

Now, about 2.7x slower – but in an easy to read, easy to modify format that saves you from having to open up your IDE and compiler.

PS:19 > $customScript = Measure-Command { C:\temp\Get-LineCount.ps1 }
PS:20 > $customScript.TotalMilliseconds / $lineCountExe.TotalMilliseconds
2.73733204860216

And to nip an annoying argument in the bud:

## Get-LineCount.rb
## Count the number of lines in in all C# files in (and below)
## the current directory

require 'find'

def filelines(file)
  count = 0
  while line = file.gets
     count += 1
  end
  count
end

def countFile(filename)
    file = File.open(filename)
    totalCount = filelines(file)
    file.close()
    totalCount
end   

totalCount = 0

files = Dir['**/*.cs']
files.each { |filename| totalCount += countFile(filename) }

puts totalCount

Which gives:

PS:21 > $rubyScript = Measure-Command { C:\temp\Get-LineCount.rb }
PS:22 > $rubyScript.TotalMilliseconds / $lineCountExe.TotalMilliseconds
3.0709602651302

Comments [5] | | # 
 Wednesday, October 04, 2006
Thursday, October 05, 2006 4:31:00 AM (Pacific Daylight Time, UTC-07:00) ( )

You may have noticed that we changed the location of the default profiles yet again! 

The one in "My Documents" is now under "WindowsPowerShell," rather than "PsConfiguration."
The one in "All User's Documents" is now under the installation root.

Since you are probably wondering why, I've updated my last explanation: The Story Behind the Naming and Location of PowerShell Profiles.

Comments [0] | | # 
 Tuesday, October 03, 2006
Tuesday, October 03, 2006 6:30:37 PM (Pacific Daylight Time, UTC-07:00) ( )

Several people have been churning out screencasts lately, and they are really great resources.

On the Channel9 front, David Aiken recently posted two DFO Show screencasts:

In addition, Doug Finke and the Lab49 team just posted an overview of some PowerShell features – Variables, Arrays, Hashtables, Functions, and PsObject.

For interactive learners, the screencast medium is a great resource.  Not only do you get to watch over somebody's shoulder, but the vocal accompaniment also provides the "Proximity Effect" benefit of working under the guidance of an expert.

Comments [3] | | #