Slides from my powershell talk

I gave an introduction to Powershell talk at the St. Louis .Net UG meeting on Monday, October 29th to about 70-80 people or so. I introduced basic concepts of powershell, talked about a few problems I had solved with it, and showed some simple scripts.

At the end of the presentation, I promised to post the slides by the end of this week, and I’m making it Wednesday night. Believe me when I say that me getting something finished early is a minor miracle 🙂

— bab

Finding installed products with uninstall instructions

I was repaving a machine the other day, and I had to load all my development tools. There were a bunch of them, and when I got finished, I noticed that IIS and SQL Server failed to install properly. So I uninstalled IIS and reinstalled it, no problem. SQL Server was another story.

I uninstalled it, and it only went part way. Then I tried to reinstall it, and it said it was already installed. I went around and around like this a few time, with a few reboots thrown in for good measure.

About this time, I started to google around for how to manually uinstall SQL Server. I came across a KB article on somewhere, and it said to crawl through the registry in HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall, inspect all the GUIDs found there for those that had a DisplayName that contained SQL Server as part of it. Once I found a matching GUID, I was to use the UninstallString to manually uninstall it.

The only problem was that there were dozens of these GUIDs in there. I sure wasn’t going to crawl through them all by hand.

So, what was a handy shell programmer to do? Well, I pulled out powershell and whipped up a script.

dir HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall | foreach-object {get-itemproperty $.PSPath} | where-object {$.DisplayName -match "SQL Server"} | select-object DisplayName,uninstallstring

Taking this script apart, piece by piece…

dir HKLM:SoftwareMicrosoftWindowsCurrentVersionUninstall lists all the registry keys beneath that location. It doesn’t just spit out a string name for each item like a Unix shell would, it returns a list of RegistryKey objects. These objects are passed to the next command in the pipeline…

foreach-object {get-itemproperty $_.PSPath} is basically a for loop over each of those objects. It looks at the PSPath property of the RegistryKey objects and calls get-itemproperty on that path to return a list of registry items.

where-object {$_.DisplayName -match “SQL Server”} selects just those registry items where the DisplayName property of the registry items match “SQL Server”. Finally, we clean up the output…

select-object DisplayName,uninstallstring essentially takes the objects that are passed to it from the previous pipeline stage and slices them into smaller pieces, creating a new object for each object passed to it. The new object contains just the two properties specified on the command, DisplayPath and UninstallString.

Running this command gives me this output:

DisplayName                                                 UninstallString
-----------                                                 ---------------
GDR 1406 for SQL Server Integration Services 2005 (64-bi... C:WINDOWSDTS9_KB932557_ENU_64Hotfix.exe /Uninstall
GDR 1406 for SQL Server Notification Services 2005 (64-b... C:WINDOWSNS9_KB932557_ENU_64Hotfix.exe /Uninstall
GDR 1406 for SQL Server Analysis Services 2005 (64-bit) ... C:WINDOWSOLAP9_KB932557_ENU_64Hotfix.exe /Uninstall
GDR 1406 for SQL Server Reporting Services 2005 (64-bit)... C:WINDOWSRS9_KB932557_ENU_64Hotfix.exe /Uninstall
GDR 1406 for SQL Server Database Services 2005 (64-bit) ... C:WINDOWSSQL9_KB932557_ENU_64Hotfix.exe /Uninstall
GDR 1406 for SQL Server Tools and Workstation Components... C:WINDOWSSQLTools9_KB932557_ENU_64Hotfix.exe /Uninstall
Microsoft SQL Server 2005 (64-bit)                          "C:Program FilesMicrosoft SQL Server90Setup Bootstra...
Microsoft SQL Server Setup Support Files (English)          MsiExec.exe /X{18C5A65B-0A39-40B5-B958-63055AFAB65C}
Microsoft SQL Server VSS Writer                             MsiExec.exe /I{50822200-2E95-4E62-A8D8-41C3B308DF5E}
Microsoft SQL Server 2005 Analysis Services (64-bit)        MsiExec.exe /I{54C2B4E9-DD13-4AA4-B09A-A6EF68F9359A}
Microsoft SQL Server Native Client                          MsiExec.exe /I{6E740973-8E71-42F9-A910-C18452E60450}
Microsoft SQL Server 2005 Integration Services (64-bit)     MsiExec.exe /I{8A52D844-0DA7-40B0-8602-0567C068C081}
Microsoft SQL Server 2005 Reporting Services (64-bit)       MsiExec.exe /I{BEE3EC3D-0C91-4A3E-A42C-7634D32968F4}
Microsoft SQL Server 2005 Backward compatibility            MsiExec.exe /I{C92556F2-4950-48CF-ABA3-F0026B05BCE8}
MySQL Server 5.0                                            MsiExec.exe /I{D84E063A-AE58-41AF-B6FC-313B12DC89A6}
Microsoft SQL Server 2005 Notification Services (64-bit)    MsiExec.exe /I{EA145881-7452-4004-80B9-971FC3D1D8D8}
Microsoft SQL Server 2005 (64-bit)                          MsiExec.exe /I{F14F2E25-99AF-42A9-977C-F6D0352DC59F}
Microsoft SQL Server 2005 Tools (64-bit)                    MsiExec.exe /I{FE7C8861-3195-4CA5-98EB-094652478192}

which is exactly what I wanted to find out in the first place. If I wanted to go further, I could run the uninstall command for these things automatically, but since I only had about 3 items I had to reinstall in my case, doing it by hand was no big deal.

Anyhow, consider this published so that when I need this trick next time, it will be waiting here, safe and sound, on my blog, ready for me to read again.

— bab

Another powershell quickie – removing all bin and obj directories beneath VS.Net projects

gci -recurse -include bin,obj . | ri -recurse

I was playing around with how to get this to work, and I couldn’t seem to figure out why these commands didn’t find the same locations to delete:

  • gci -recurse -include bin,obj .
  • ri -recurse -force -include bin,obj -whatif .

I finally got so baffled that I RTFMed remove-item, and there was my answer. In the fine print, nestled away in an example that did what I was looking for, and in the documentation for the recurse parameter was my answer…

-recurse <SwitchParameter>
Deletes the items in the specified locations and in all child items of the locations.

The Recurse parameter in this cmdlet does not work properly.

Ah ha! That’s when I went to the format that I finally settled on, and everything worked.

Another blog posting mostly written to help me remember how I solved this problem the next time I encounter it!

— bab

find -name foo.* | xargs grep “find_me”

[Update from Brad Wilson and Scott Dukes]

I’ve been wanting a powershell script to replace my favorite unix command for ages, and I took a stab at it today. This got me very close to what I wanted yesterday, which was just the names of files where the matches occurred.

get-childitem -include foo.* -recurse | where-object { get-content $_ | select-string find_me }

Scott Dukes pointed out that select-string would take the FileInfo object that was passed to it and search through the contents for the string file_me, and this would give me the exact same kind of output my unix command used to. Thanks, Scott! This command was exactly what I needed.

Brad went way past this and showed some very interesting other changes to the script to do some cool things.

Thanks again to both of them for correcting this PowerShell newbie. I’ve been a long-time Unix scripting weenie, with bash code flowing out my fingertips at will (in XEmacs of course :)), but I think PowerShell just blows that away.

— bab