Browsing Posts in PowerShell

A question I often get asked is why psake does not include something similar to NAnt’s <replacetokens>. The reason is because it’s so darn easy to do it in PowerShell. Given foo.txt.template:

@@foo@@ is @@bar@@!!!

The following script will perform the replacement:

# replace.tokens.ps1
$foo = 'PowerShell'
$bar = 'da bomb'
(cat foo.txt.template) -replace '@@foo@@', "$foo" `
                       -replace '@@bar@@', "$bar" `
                       > foo.txt

(Note the backticks (`) at the end of the line to denote continuation.)

This script will produce:

PowerShell is da bomb!!!

You could easily write a function that would take care of the nitty gritty details. The @@var@@ is arbitrary. You could use any sequence you like. You can even perform regex matches in the @@var@@ expression if needed. Note the double quotes around "$foo". This is PowerShell for performing variable replacements in strings. So "$foo" results in the word PowerShell whereas ‘$foo’ results in the word $foo.

So there you have it. Token replacement built right into PowerShell. Happy Scripting!

psake It is with great pleasure that I announce psake v4.00, which you can download here. The project has grown up a great deal in the last few months. More projects are using psake for orchestrating their builds and we have more developers submitting patches. I’m really pleased with how psake is growing up. GitHub has become our coordination point for the project, which you can find here, and we will be retiring the Google Code site. Jorge has started work on a FAQ, which will make it easier for developers to get started with psake. Look for it on the GitHub wiki in the next few weeks.

What’s new in psake v4.00?

  • Exec helper function
  • .NET 4.0 support
  • 64-bit support
  • psake.ps1 helper script
  • Support for parameters & properties
  • Support for nested builds
  • Tab expansion
  • Invoke default script
  • Various minor bug fixes

Exec Helper Function

The exec helper function was included in the psake v2.02 patch, but it bears mentioning again. If you are executing a command line program (such as msbuild.exe, aspnet_compiler.exe, pskill.exe, …) rather than a PowerShell function, it will not throw an execption on failure, but return a non-zero error code. We added the exec helper function, which takes care of checking the error code and throwing an exception for command line executables.

task Compile -depends Clean {
  exec { msbuild Foo.sln }
}

You can find out more details here.

.NET 4.0 Support

psake still defaults to .NET Framework 3.5, but you can specify another framework to use from the command line:

invoke-psake examples\default.ps1 -framework 4.0

Or from within your script:

$framework = '4.0'

task default -depends MsBuild

task MsBuild {
  exec { msbuild /version }
}

64-bit Support

You can now specify whether you want to use 32-bit (x86) or 64-bit (x64) tools when building your projects.

invoke-psake examples\default.ps1 -framework 3.5x86
invoke-psake examples\default.ps1 -framework 3.5x64

If you don’t specify x86 or x64 after the framework version, psake selects the framework bitness based on whether you’re running from a 32-bit or 64-bit PowerShell prompt. (On 64-bit Windows, the default PowerShell prompt is 64-bit. If you want a 32-bit prompt, launch “Windows PowerShell (x86)”. Valid values for the framework are ’1.0′, ’1.1′, ’2.0′, ’2.0×86′, ’2.0×64′, ’3.0′, ’3.0×86′, ’3.0×64′, ’3.5′, ’3.5×86′, ’3.5×64′, ’4.0′, ’4.0×86′, and ’4.0×64′.

psake.ps1 Helper Script

Because psake is a PowerShell module, you have to import the module before using it.

import-module .\psake.psm1
invoke-psake examples\default.ps1
# do some work
invoke-psake examples\default.ps1
# do some more work
remove-module psake # when done, remove psake or close your PS prompt

If you want to use the psake.psm1 stored in a particular project’s repository, you have to remember to import the correct version of psake from that project. You also need to create a ps1, bat, or cmd file for your continuous integration server so that the correct version of psake is registered to orchestrate the build. We have standardized this using a psake.ps1 helper script:

# psake.ps1
# Helper script for those who want to run
# psake without importing the module.
import-module .\psake.psm1
invoke-psake @args
remove-module psake

With this script, you can now execute psake without first importing the module.

.\psake examples\default.ps1 test

You can read about the splatting operator (@) here if you’re wondering what invoke-psake @args does.

Support for Parameters and Properties

Invoke-psake has two new options, –parameters and –properties. Parameters is a hashtable passed into the current build script. These parameters are processed before any ‘Properties’ functions in your build scripts, which means you can use them from within your Properties.

invoke-psake Deploy.ps1 -parameters @{server=’Server01’}

# Deploy.ps1
properties {
  $serverToDeployTo = $server
}

task default -depends All

# additional tasks

Parameters are great when you have required information. Properties on the other hand are used to override default values.

invoke-psake Build.ps1 -properties @{config='Release'}

# Build.ps1
properties {
  $config = 'Debug'
}

task default -depends All

# additional tasks

Support for Nested Builds

You can now invoke build scripts from within other build scripts. This allows you to break large, complex scripts into smaller, more manageable ones.

task default -depends RunNested1, RunNested2

task RunNested1 {
  Invoke-psake .\nested\nested1.ps1
}

task RunNested2 {
  Invoke-psake .\nested\nested2.ps1
}

Tab Expansion

Dusty Candland implemented PowerShell tab expansion for psake. You can find instructions on setting up tab expansion in ./tabexpansion/Readme.PsakeTab.txt in the download. Once configured, you can:

tab completion for file name: psake d<tab> -> psake .\default.ps1
tab completion for parameters: psake -t<tab> -> psake -task
tab completion for tasks: psake -task c<tab> -> psake -task Clean

You can find more details on Dusty’s blog here. Excellent addition! Thanks, Dusty.

Invoke Default Script

Jason Jarrett provided this welcome fix, which allows you to execute tasks without specifying the default build file name (default.ps1).

invoke-psake Compile # Executes the Compile task in default.ps1

Previously you had to specify invoke-psake default.ps1 Compile. You could only omit default.ps1 if you were running the default task.

Big Thanks!

Thanks to everyone who contributed to this release, especially Jorge Matos who contributed many of the new features noted above. If you have any questions, please join the psake-users Google Group. If you’re interested in contributing to the ongoing development, we also have a psake-dev Google Group. Happy scripting, everyone!

P.S. Wondering what happened to psake v3.00? It’s chilling with its friends EF v2 and EF v3…

BatmanNo, this post is not a tribute to the fabulously kitschy Batman TV series (1966-1968) starring Adam West and Burt Ward. Or a tribute to the onomatopoeic sounds for which it and the Batman comics were famous. This show did however come to mind when I was trying to solve a PowerShell problem and ran across the wonderfully-named splatting (@) operator introduced in PowerShell v2. Before we get to the splatting operator, let’s look at the problem that it was designed to solve.

With psake v2 came the change from a PowerShell script to a PowerShell module. Modules provide a lot of advantages over a simple script. For psake the compelling advantages were better control over scoping and better integration with PowerShell’s help system. One disadvantage was that you now had to first import the module before you could use psake.

image

ASIDE: If you’re wondering about the “James@EDDINGS psake [master +0 ~1 -0]>” stuff, I’ve installed Mark Embling’s awesome PowerShell Git Prompt, which is simply a custom PowerShell prompt. It tells me that my user is James, I’m logged into my main dev machine (EDDINGS), I’m in the psake directory (c:\dev\oss\psake) – though I only display the last part of the path for brevity, I’m on the “master” branch, I have no pending additions (+0), no pending changes (~0), and no pending deletions (-0). (I need to see if I can hack in how many commits forward or back I am from a tracked remote.) Everything in brackets is omitted if it isn’t a Git directory. Another good set of Git/PowerShell scripts is Jeremy Skinner’s PowerShell Git Tab Expansion for completing common command names, branch names, and remote names. If you are using Git and PowerShell, I would highly recommend both Mark’s and Jeremy’s scripts. If you don’t want to copy/paste them together, you can grab them from my random collection of PowerShell scripts here.

Note how we had to first call “import-module” before we could use psake. For some people, they install the latest version of psake in some well-known location, import the module, and then run it from there until the next update comes out. For others (e.g. me), we like to version psake along with our source code and other dependencies. Importing a project-specific copy of psake becomes a headache very quickly. So I wrote a little shim script to register psake, run it, and then unregister it.

# Helper script for those who want to run
# psake without importing the module.
import-module .\psake.psm1
invoke-psake $args
remove-module psake

Seems reasonable enough. We simply pass along the script arguments ($args) to the invoke-psake command and everything should be fine.

image

OK. What happened? PowerShell did what we told it to. It called the function, invoke-psake, with an array as its first parameter rather than using the array as the list of parameters as we intended. Let’s fix that.

# Helper script for those who want to run
# psake without importing the module.
import-module .\psake.psm1
invoke-psake $args[0] $args[1]
remove-module psake

One little problem.

image

Note that we left out the task (“clean” previously) so that psake would use the default. Rather than using the default, invoke-psake has been passed a null argument for the task. We could fix this by detecting null arguments in invoke-psake and explicitly specifying the defaults. It’s ugly because we couldn’t use PowerShell’s syntax for specifying defaults, but it would work. Another problem is that we would need to add as many $args[N] as we expected to receive arguments. A messy solution all around.

Fortunately PowerShell v2 has an elegant solution to this problem called the splatting operator, denoted by @. The splatting operator binds an array to the argument list of a function.

# Helper script for those who want to run
# psake without importing the module.
import-module .\psake.psm1
invoke-psake @args
remove-module psake

Note the subtle change. Rather than using $args we use @args.

image

Success! And it’s not just for passing arguments from one script to another. You can create arr

 image

Note the call to “Add $addends” where PowerShell called the Add function once for every item in the array. Not what we intended. “Add @addends” using the splatting operator gave us the expected result. You can even use a hashtable to splat named parameters.

image

Note that the answer was 1 (e.g. 11 % 10) and not 10 (e.g. 10 % 11). The splatting operator properly bound the value 11 to the x parameter and 10 to the y parameter, just as it was in the hashtable.

The splatting operator provides us with a tremendous amount of flexibility in manipulating function and script arguments. It’s a useful tool to add to your PowerShell arsenal. Go forth and SPLAT!

I must admit that I don’t much care for PowerShell’s default behaviour with respect to errors, which is to continue on error. It feels very VB6 “On Error Resume Next”-ish. Given that it is a shell scripting language, I can understand why the PowerShell team chose this as a default. Fortunately you can change the default by setting $ErrorActionPreference = ‘Stop’, which terminates execution by throwing an exception. (The default value is Continue, which means the script prints the error and continues executing.) Unfortunately this only works for PowerShell commands and not external executables that return non-zero error codes. (In the shell world, a return code of zero (0) indicates success and anything else indicates failure.)

Take the following simple script:

'Starting script...'
$ErrorActionPreference = 'Stop'
ping -badoption
"Last Exit Code was: $LastExitCode"
rm nonexistent.txt
'Finished script'

image

Notice how execution continued after the ping command failed with an exit code of one (1) even though we have $ErrorActionPreference set to ‘Stop’. Also notice that the rm command, which is an alias for the PowerShell command, Remove-Item, did cause execution to abort as expected and ‘Finished script’ was never printed to the console. The discrepancy in error handling between PowerShell commands and executables is annoying and forces us to constantly think about what we’re calling – a PowerShell command or an executable. The obvious solution is:

'Starting script...'
$ErrorActionPreference = 'Stop'
ping -badoption
if ($LastExitCode -ne 0) { throw 'An error has occurred...' }
rm nonexistent.txt
'Finished script'

image

The error handling code adds a lot of noise, IMHO, and feels like a throwback to COM and HRESULTs. Can we do better? Jorge Matos, one of the psake contributors came up with this elegant helper function:

function Exec([scriptblock]$cmd, [string]$errorMessage = "Error executing command: " + $cmd) { 
  & $cmd 
  if ($LastExitCode -ne 0) {
    throw $errorMessage 
  } 
}

Note the “& $cmd” syntax. $cmd is a scriptblock and & is used to execute the scriptblock. We can now re-write our original script as follows. (N.B. Exec function is elided for brevity.)

'Starting script...'
$ErrorActionPreference = 'Stop'
exec { ping -badoption }
rm nonexistent.txt
'Finished script'

image

The script now terminates when the bad ping command is executed. We do have to remember to surround executables with exec {}, but this is less noise IMHO than having to check $LastExitCode and throwing an exception.

For those of you using psake for your builds, the Exec helper function is included in the latest versions of the psake module. So you can use it in your build tasks to ensure that you don’t try to run unit tests if msbuild fails horribly. smile

Happy Scripting!

image

A few announcements… First the big one. Many people have been using psake – both the PowerShell 1.0- and 2.0-compatible versions – in production without any significant issues. For that reason, we have released psake v1.00 (compatible with PowerShell 1.0). The only difference between psake v1.00 and psake v0.23 is the version number. My friend, Ayende, has a great example of converting Rhino Mocks build to use psake.

http://psake.googlecode.com/files/psake-v1.00.zip

We have released psake v2.01 (compatible with PowerShell 2.0). (This was formerly called psake v0.24, “Jorge”, and psake-ps2.) A big thanks to Jorge Matos for all his work on psake v2.01.

http://psake.googlecode.com/files/psake-v2.01.zip

A few minor changes… The source code for psake has been moved to GitHub and the SVN repository at Google Code has been retired. We will still be using Google Code for bug tracking, wiki pages, etc. If you want the latest source code, you can always download a zip file for master (aka trunk in SVN terms) – or any tags/branches – from:

http://github.com/JamesKovacs/psake

Note that there is no need to install Git to download the latest package as GitHub will create the appropriate zip file on the fly.

If you have some great idea, you can download the git repo from git://github.com/JamesKovacs/psake.git or http://github.com/JamesKovacs/psake.git. (msysgit is the Git package of choice for Windows. You can download it from http://code.google.com/p/msysgit/.) I would encourage you to read Jeremy Skinner’s excellent guide for contributing to MvcContrib via GitHub. Just mentally replace “MvcContrib” with “psake”, though I’d encourage you to contribute to MvcContrib too. :)

I would like to offer lots of kudos to my collaborators/conspirators on the project. Jorge Matos has been instrumental in updating/improving psake to use the new PowerShell v2 features. Thanks to Shaun Becker for patches and answering newsgroup questions. And thanks to Eric Hexter for his assistance in moderating the psake-users Google Group. I am heartened and thankful for the willing collaboration on this project and am excited to watch it grow. If you have any questions, please do not hesitate to ask.

Going forward, we are retiring psake v1.00 and focusing on psake v2.00. If there is demand for a PowerShell v1-compatible version of psake, we will create a branch based on the v1.00 tag, but we will mostly be focused on the PowerShell v2-compatible version (aka psake v2.00). So your next question probably is…

What’s New in psake v2.01?

(from Jorge Matos)

The main difference is that psake v2.01 has been re-written as a module that contains advanced functions.  Someone using the module could either run the import-module command with the path to the module file (i.e. import-module .\psake.psm1) or (my preference) you can copy the psake.psm1 into a folder called psake into the “Modules” folder in your profile directory (you may have to create it if it’s not there) or your machine-wide “Modules” directory:

i.e. Profile Directory:

C:\Users\Jorge\Documents\WindowsPowerShell\Modules\psake

i.e. Machine-wide Modules folder:

C:\Windows\System32\WindowsPowerShell\v1.0\Modules\psake

Once the psake folder is created and you’ve copied the psake.psm1 file into it – restart PS and type “import-module psake” – PS will find the module and load it automatically.  What I’ve done is add the “Import-Module psake” to my profile script so that it is loaded everytime I startup PS.

Module Benefits:
  1. Build scripts don’t need to know where psake is installed, they just call Invoke-psake and it works.
  2. Encapsulation… Global variables are no longer required since they can be private to a module unless explicitly exported (I haven’t gotten around to actually changing the psake code to not use global variables yet).
  3. Modules can be unloaded if needed which removes all the code and variables from memory.
Advanced Functions:

The other big difference is that the “Invoke-psake” and “Task” functions have been converted into Advanced Functions which basically means you can take advantage of comment help which means you can type help invoke-psake and you will get back real help with examples.

Minor changes:
  1. Coding style is different.
  2. Try/Catch is used instead of the “Trap” statement.
  3. Got rid of the “exec” function.
  4. You can now define “Pre” and “Post” actions for a task.
  5. You can define how the task name will be formatted.
  6. You can define a “TaskSetup” function that will be executed before every task (took that from NUnit).
  7. You can define a “TaskTearDown” function that will be executed after every task (took that from NUnit too).
  8. Create a global variable called “psake_buildSucceeded” that will be set to true if the build succeeds – scripts can check this.
  9. Also added a “$noexit” switch to Run-Psake so that the function will not use the exit() function so that you can test a build script at the command line without PS closing down (the default behavior when the build fails is to call exit(1) so that the calling code can determine if the build failed or not).
  10. The psake-buildTester.ps1 had to be changed slightly in order for it to call the Invoke-psake function.
  11. Added more examples in the .\examples folder for POST conditions, PRE and POST Actions, etc.

Happy (build) scripting!

At first glance, PowerShell appears to be yet another command shell with the interesting twist that you pipe objects between commands rather than strings. But there is more to PowerShell than that. One fascinating area is PowerShell Providers. (PowerShell Providers aren’t anything new as they’ve been there since v1. So I’m not the first – nor will I be the last – to blog about them, but hopefully some folks starting out with PowerShell find this useful…)

We’ll start with a simple example using “ls” to list the contents of a directory:

ls c:\

Now “ls” is just a two-letter version of “dir” and both are aliases for “Get-ChildItem”. How do I know that?

ls alias:

image

This prints out all current aliases. That funky “alias:” is a PowerShell provider. If you want a specific alias, you can “ls alias:ls” or “ls alias:dir”.

image

To get a list of currently installed PowerShell providers, you can use Get-PSProvider:

image

You should notice a few interesting entries there. You want a list of environment variables? The Environment provider does the trick:

image

Note that providers aren’t read-only. Let’s say you want to temporarily add a directory to your path. In cmd.exe, you would do the following:

set PATH = %PATH%;<EXTRA_DIR>;

In PowerShell, you use the Environment provider:

$env:PATH += “;<EXTRA_DIR>”;”

Notice the env: prefix that tells PowerShell that the variable is handled by the Environment provider. (Notice above that “Env” is listed as the “drive” for the Environment provider.)

Let’s explore some more…

 

image

Notice that changing drive letters is actually handled by the Function provider and are just commands.

It gets more interesting with the Registry provider through which you can access HKEY_LOCAL_MACHINE via hklm: and HKEY_CURRENT_USER via hkcu:.

image

You even get tab completion while typing. (Try ls hkcu:<TAB><TAB><TAB> to see various subkeys for HKEY_CURRENT_USER.) And assuming that you have write permission to the registry keys, you can set them too!

PowerShell providers aren’t limited to those shipped by Microsoft. You can in fact write your own, though I’ve never tried it. People have written their own providers for everything from SharePoint to Subversion.

So go check out PowerShell providers. Happy Scripting!

psake Last night I gave a presentation on psake and PowerShell to the Virtual ALT.NET (VAN) group. I had a fun time demonstrating how to write a psake build script, examining some psake internals, discussing the current state of the project, and generally making a fool of myself by showing how much of a PowerShell noob I really am. I believe that the presentation was recorded and will be posted online in the next few days. Then you too can see me fumbling around trying to remember PowerShell syntax. I consider myself a professional developer when it comes to many areas, but in terms of PowerShell I am a hack who learns just enough to get the job done.

As promised, here are the links from the meeting…

psake Resources

Project Homepage

Users mailing list

Dev mailing list

PowerShell Resources

PowerShell Cheat Sheet
 

Windows PowerShell in Action (book)
 

Windows PowerShell Team Blog

On Twitter, I have a search for #psake. If you have a question, comment, or quibble about psake, you can use the #psake hashtag or @JamesKovacs to get my attention.

P.S. A number of people expressed interest in some of my dev-related PowerShell scripts, such as removing unversioned files from a SVN working copy, updating all SVN working copies off a common directory, cleaning a solution, … I’ll be putting them in a publicly accessible location soon and blogging about those scripts. So please be patient and don’t adjust your sets.

I’ve been having fun writing about my adventures in PowerShell. I would like to thank everyone for their encouragement and feedback. Something that I haven’t explicitly stated – which should go without saying as this is a blog – is that I am not a PowerShell expert. This is one man’s journey learning about PowerShell. I consider myself an expert on C#, .NET, and many other things, but as for PowerShell, I am a hacker. I learn enough to get the job done.

Yes, I wrote psake, which is a cool little PowerShell-based build tool, if I do say so myself. I wrote it in part to learn more about PowerShell and what was possible. (I surprised myself that I was able to write a task-based build system in a few hours with about 100 lines of PowerShell, ignoring comments.)

If you’re looking for PowerShell gospel, I would recommend checking out the Windows PowerShell Blog (the blog of Jeffrey Snover and the rest of the PowerShell team), Windows PowerShell in Action by Bruce Payette, the PowerScripting Podcast, or any of the myriad PowerShell MVP blogs. They are the experts. I’m just a hacker having fun.

With that disclaimer, I hope that by documenting my PowerShell learnings in public, I will help other developers learn PowerShell. I know that I am learning great things about PowerShell from my readers. In Getting Started with PowerShell – Developer Edition, I lamented the lack of grep. My friend, Chris Tavares – known for his work on Unity and ASP.NET MVC – pointed out that Select-String can perform similar functions. Awesome! Then in PowerShell, Processes, and Piping, Jeffrey Snover himself pointed out that PowerShell supports KB, MB, and GB – with TB and PB in v2 – so that you can write:

get-process | where { $_.PrivateMemorySize –gt 200MB }

rather than having to translate 200MB into 200*1024*1024 as I originally did. Fantastic!

In Writing Re-usable Scripts with PowerShell, wekempf, Peter, and Josh discussed the merits of setting your execution policy to Unrestricted. I corrected the post to use RemoteSigned, which means that downloaded PowerShell scripts have to be unblocked before running, but local scripts can run without requiring signing/re-signing. Thanks, guys. I agree that RemoteSigned is a better option.

Let’s talk security for a second. I am careful about security. I run as a normal user on Vista and have a separate admin account. When setting up teamcity.codebetter.com, the build agent runs under a least privilege account, which is why we can’t run NCover on the build server yet. (NCover currently requires admin privs, though Gnoso is working on fixing that in short order.) (Imagine if we did run builds as an Administrator or Local System. Someone could write a unit test that added a new user with admin privs to the box, log in remotely and start installing bots, malware, and other evil.) So I tend to be careful about security.

Now for my real question… What is the threat model for PowerShell that requires script signing? Maybe I’m being really dense here, but I don’t get it. Let’s say I want to do something really evil like formatting your hard drive. I create a PowerShell script with “format c:” in it, exploit a security vulnerability to drop it onto your box, and exploit another security vulnerability to launch PowerShell to execute the script. (Or I name it the same as a common script, but earlier in your search path, and wait for you to execute it.) But you’ve been anal-retentive about security and only allow signed scripts. So the script won’t execute. Damn! Foiled again! But wait! Let me just rename it from foo.ps1 to foo.cmd or foo.bat and execute it from cmd.exe. If I can execute code on your computer, there are easier ways for me to do bad things than writing PowerShell scripts. Given that we can’t require signing for *.cmd and *.bat files as this would horribly break legacy compatibility, what is the advantage of requiring PowerShell scripts to be signed by default? Dear readers, please enlighten me!

UPDATE: Joel “Jaykul” Bennett provided a good explanation in the comments. I would recommend reading:

http://blogs.msdn.com/powershell/archive/2008/09/30/powershell-s-security-guiding-principles.aspx

as it exlains the PowerShell Team’s design decision. The intention wasn’t to force everyone to sign scripts, but to disable script execution for most users (as they won’t use PowerShell), but allow PowerShell users to opt into RemoteSigned or Unrestricted as they so choose. Script signing is meant for administrators to set group policy and use signed scripts for administration (as one example use case of script signing).

Thanks again, Joel! That was faster than sifting through the myriad posts on script signing trying to find the reasoning behind it. Once again, the advantages of learning as a community!

Continuing on from last time, I will now talk about writing re-usable scripts in PowerShell. Any command that we have executed at PowerShell command line can be dropped into a script file. I have lots of little PowerShell scripts for common tasks sitting in c:\Utilities\Scripts, which I include in my path. Let’s say that I want to stop all running copies of Cassini (aka the Visual Studio Web Development Server aka WebDev.WebServer.exe).

Stop-Process -name WebDev.WebServer.exe -ErrorAction SilentlyContinue

This will terminate all running copies of the above-named process. ErrorAction is a common parameter for all PowerShell commands that tells PowerShell to ignore failures. (By default, Stop-Process would fail if no processes with that name were found.)

We’ve got our command. Now we want to turn it into a script so that we don’t have to type it every time. Simply create a new text file with the above command text called “Stop-Cassini.ps1” on your desktop using the text editor of your choice. (The script can be in any directory, but we’ll put it on our desktop to start.) Let’s execute the script by typing the following at the PowerShell prompt:

Stop-Cassini

Current dirctory not in search path by default

What just happened? Why can’t PowerShell find my script? By default, PowerShell doesn’t include the current directory in its search path, unlike cmd.exe. To run a script from the current directory, type the following:

.\Stop-Cassini

Another option is to add the current directory to the search path by modifying Computer… Properties… Advanced… Environment Variables… Path. Or you can modify it for the current PowerShell session using:

$env:Path += ‘.\;’

($env: provides access to environment variables in PowerShell. Try $env:ComputerName, $env:OS, $env:NUMBER_OF_PROCESSORS, etc.)

You could also modify your PowerShell startup script, but we’ll talk about that in a future instalment. Let’s run our script again:

ExecutionPolicy error

No dice again. By default, PowerShell does not allow unsigned scripts to run. This is a good policy on servers, but is a royal pain on your own machine. That means that every time you create or edit a script, you have to sign it. This doesn’t promote the use of quick scripts for simplifying development and administration tasks. So I turn off the requirement for script signing by running the following command from an elevated (aka Administrator) PowerShell prompt:

Set-ExecutionPolicy Unrestricted

Set-ExecutionPolicy RemoteSigned

Set-ExecutionPolicy succeeded

If this command fails with an access denied error:

Set-ExecutionPolicy failed

then make sure that you launched a new PowerShell prompt via right-click Run as administrator…

Third time is the charm…

Success!

We are now able to write and use re-usable scripts in PowerShell. In my next instalment, we’ll start pulling apart some more complicated scripts that simplify common developer tasks.

UPDATE: As pointed out by Josh in the comments, setting your execution policy to RemoteSigned (rather than Unrestricted) is a better idea. Downloaded scripts will require you to unblock them (Right-click… Properties… Unblock or ZoneStripper if you have a lot) before execution. Thanks for the correction.

Last time, I discussed why you as a developer might be interested in PowerShell and gave you some commands to start playing with. I said we’d cover re-usable scripts, but I’m going to delay that until next post as I want to talk more about life in the shell…

PowerShell feels a lot like cmd.exe, but with a lot more flexibility and power. If you’re an old Unix hack like me, you’ll appreciate the ability to combine (aka pipe) commands together to do more complex operations. Even more powerful than Unix command shells is the fact that rather than inputting/outputting strings as Unix shells do, PowerShell inputs and outputs objects. Let me prove it to you…

  1. At a PowerShell prompt, run “get-process” to get a list of running processes. (Remember that PowerShell uses single nouns for consistency.)
  2. Use an array indexer to get the first process: “(get-process)[0]” (The parentheses tell PowerShell to run the command.)
  3. Now let’s get really crazy… “(get-process)[0].GetType().FullName”

As a .NET developer, you should recognize “.GetType().FullName”. You’re getting the class object (aka System.Type) for the object returned by (get-process)[0] and then asking it for its type name. What does this command return?

image

That’s right! The PowerShell command, get-process, returns an array of System.Diagnostics.Process objects. So anything you can do to a Process object, you can do in PowerShell. To figure out what else we can do with a Process object, you can look up your MSDN docs or just ask PowerShell itself.

get-member –inputObject (get-process)[0]

Out comes a long list of methods, properties, script properties, and more. Methods and properties are the ones defined on the .NET object. Script properties, alias properties, property sets, etc. are defined as object extensions by PowerShell to make common .NET objects friendlier for scripting.

Let’s try something more complex and find all processes using more than 200MB of memory:

get-process | where { $_.PrivateMemorySize –gt 200*1024*1024 }

Wow. We’ve got a lot to talk about. The pipe (|) takes the objects output from get-process and provides them as the input for the next command, where – which is an alias for Where-Object. Where requires a scriptblock denoted by {}, which is PowerShell’s name for a lambda function (aka anonymous delegate). The where command evaluates each object with the scriptblock and passes along any objects that return true. $_ indicates the current object. So we’re just looking at Process.PrivateMemorySize for each process and seeing if it is greater than 200 MB.

Now why does PowerShell use –gt, –lt, –eq, etc. for comparison rather than >, <, ==, etc.? The reason is that for decades shells have been using > and < for input/output redirection. Let’s write to the console:

‘Hello, world!’

Rather than writing to the console, we can redirect the output to a file like this:

‘Hello, world!’ > Hello.txt

You’ll notice that a file is created called Hello.txt. We can read the contents using Get-Content (or its alias, type).

get-content Hello.txt

image

Since > and < already have a well-established use in the shell world, the PowerShell team had to come up with another syntax for comparison operators. They turned to Unix once again and the test command. The same operators that have been used by the Unix test command for 30 years are the same ones as used by PowerShell.*

So helpful tidbits about piping and redirection…

  • Use pipe (|) to pass objects returned by one command as input to the next command.
    • ls | where { $_.Name.StartsWith(‘S’) }
  • Use output redirection (>) to redirect the console (aka stdout) to a file. (N.B. This overwrites the destination file. You can use >> to append to the destination file instead.)
    • ps > Processes.txt
  • Do not use input redirection (<) as it is not implemented in PowerShell v1. smile_sad

So there you have it. We can now manipulate objects returned by PowerShell commands just like any old .NET object, hook commands together with pipes, and redirect output to files. Happy scripting!

* From Windows PowerShell in Action by Bruce Payette p101. This is a great book for anyone experimenting with PowerShell. It has lots of useful examples and tricks of the PowerShell trade. Highly recommended.