Discover Powershell

For ages now, Windows users have been yearning for a better console and a good scripting language for their operating system. I’m not going to get into the history of MS’ attempts at fixing this (VBScript and WSH friends). Instead I’ll delve right into what seems to be the (bright) future of scripting under Windows: Powershell.

In this tutorial I’ll try to explain briefly what Powershell is and give some examples. This is not meant to be a complete tutorial, however: for that you’ll have to scroll all the way down to the references section.

Powershell (originally known as Monad) is the future of Windows scripting. It will be bundled with Windows Server 2008 and probably the next desktop versions as well (maybe even Vista SP1?) Anyway, meanwhile Powershell can be downloaded for use with XP SP2, Vista, and Server 2003.

For now PS does not replace the console you know and hate. It only styles it a little bit. Skeptics could remind me that a dog with lipstick is still a dog. I’d say read on and decide for yourself after you’ve given Powershell a try. So here it is:

ps1.png

Tremendous, it’s blue instead of black! This is all very well, but not too convincing yet, I guess. Let’s see in more detail what there is to Powershell.

Basics

Before I dive into features, I’d like to quickly cover some basics. First of all, go Get Powershell, and then grab MS’ Powershell quick reference and print it.

Variables

1
$variable = <expression>

Strings

1
2
"a string that will expand a $variable"
'a string that not will expand a $variable'

Paths

Both \ and / are accepted where paths are expected. This also means that parameters don’t start with a / anymore, they start with a -.

You can now navigate to UNC paths, as opposed to the old console:

1
cd \\someserver\someshare

Case sensitivity

PS keeps with Windows’ traditional way of being case insensitive. This is valid for the file names as well as the commands/parameters/arguments.

Keeping it quiet

1
dir > $null

Being verbose (you, that is)

1
#This is a comment!

There are no multiline comments but you can use multi-line strings. It’s patchy but it works (don’t use @” and “@ for multi-line comments, see Strings a couple lines up to know why).

1
2
3
@'
loooong comment
'@ > $null

“New” navigation trick

Need to do a couple of things in a directory then get back? The days of saving the current directory manually are over:

1
2
3
pushd 'c:\Windows\System32'
#Do evil stuff
popd

Actually I just realized that the current console also supports this! Shame on me :-)

Control flow

The usual suspects are there, look at the Quick Reference. While you’re there, look at “How to Make Comparisons”, as well.

Finding out more

Along the way, if you’re eager to find out more about what’s I’m talking about, you can always use the following commands.

Getting help

help <commandname>, help <aliasname> or the shorthand <command> -?.

All will bring up basic help with all the command’s parameters. If you need more information, you can append -detailed or -full to get a more complete explanation, examples and so on.

If you need help with concepts (other than commands) about Powershell, you might have luck with about_

1
2
3
help about_regular_expression
help about_reg*
help about_*

Members of an object

When you’re wondering what kind of object you’re dealing with, you can always use the command Get-Member. It will start by displaying the class of the object and then display all its members.

1
2
$var = "some string"
$var | Get-Member

Features

Powershell works hard to make Windows and Posix users feel right at home

Powershell syntax uses aliases. So for example dir and ls will work (both are aliases for Get-ChildItem). However, in order to minimize nightmares all around the world, the respective aliases’ original arguments are not necessarily preserved. Instead you’ll have to check PS’ new way of doing things (try the help mentioned earlier). To keep the same example, instead of writing

dir /s *.txt

or

ls -R . *.txt

With PS one would either type:

ls . *.txt -recurse

or dir . *.txt -recurse

or to completely swallow the PS pill: GetChild-Item . *.txt -recurse

Powershell piping is object-oriented

Wow! So PS event has some (20 years old) buzzword compliance? (That was my first thought, at least.) Seriously, this has some nice advantages over returning plain text. Let’s remember some of the side effects of commands returning text and piping text between commands:

  • On most individual commands, lots of arguments exist only to format the text (ls -lb), rather than to modify the behavior of the command (e.g. flat vs recursive);
  • You’ll sometimes have to resort to regexes to understand the text blob a command returns, introducing accidental complexity to your scripts (or yellow code).

Now let me explain with a couple of examples what is meant by piping objects.

First of all, it must be mentioned that all objects have a default representation. So a simple dir would still simply display the list of files in the current directory.

Behind the scenes, however, the command dir returns a list of objects. If you want to format it differently, you can pipe it to Format-List, Format-Table, Format-Wide or Format-Custom (fl, ft, fw or fc):

dir | fl Name, CreationTime, LastAccessTime

You can put the result of your dir in a variable:

$myFiles = dir

Now $myFiles is an array of objects, likely containing instances of System.IO.FileInfo and System.IO.DirectoryInfo.

You can reuse that array and iterate over it, filter through it, sort it and all kinds of neat stuff like that. $myFile[0] gets you the first result of the array. Here’s an example of filtering :

Get-Alias | where-object {$_.Definition -match 'Get-Childitem'}

That’s a mouthful! Let’s explain what all this is. Get-Alias returns all aliases currently defined and we filter them with Where-Object to display only Get-ChildItem’s aliases. Namely dir, ls and gci. The Perl-like $_ refers to the object being handled at each iteration. We check that its Definition matches 'Get-ChildItem' with the -match parameter , which accepts a regex.

Now we sort:

1
2
Get-Alias | Sort-Object #Sorts by name
Get-Alias | Sort-Object Definition

Let’s do a quick example of foreach as well, before moving on:

dir | foreach-object { $totalsize = $totalsize + $_.Length } $totalsize

Note: since $totalsize didn’t already exist, it’s been initialized to 0.

Powershell is very well integrated with Windows

The registry

Here’s one feature that made me smile from ear to ear: you can browse and read the registry like you browse and read files on your filesystem.

1
2
cd hklm:software\Microsoft
dir

This approach is Ok for exploration. However I find that reading the value directly is often enough:

$key = Get-Item 'hklm:software\Microsoft\Internet Explorer'

Now we can get/set values

$key.GetValue('Build')

Or work with subkeys

$subkey = $key.OpenSubkey('International')

Processes and services

You can also see what’s happening on the computer:

1
2
$processes = Get-Process
$processes

To display all properties of a given process

$processes[0] | fl *

To find processes with a specific characteristic

Get-Process | Where-Object { $_.Product -match 'Microsoft' }

or

Get-Process | Where-Object { $_.Threads.Count -gt 20 }

If you want to see what services are configured:

Get-Services

Which ones are running only:

Get-service | Where-Object {$_.Status -eq 'Running'}

You can also manipulate them

1
2
3
Get-Service 'wuauserv' | fl *
Stop-Service 'wuauserv'
Start-Service 'wuauserv'

File formats

A couple of nice file formats are supported by default.

txt

Of course!

1
2
3
dir | Out-File 'dir.txt' -noClobber
dir >  'dir.txt'
dir >>  'dir.txt'

Note: Out-File is just for when you need to use the command’s additional parameters.

csv

1
2
dir |  Export-Csv 'dir.csv'
$dir = Import-Csv 'dir.csv'

Other file formats

You can also work with other file formats, such as XML, with Powershell. But I guess for an introduction it’s starting to get a little too deep :-)

Other specific uses

For administrators

You can use Powershell to help with administrations tasks:

  • User and group management

  • Network administration tasks and monitoring using WMI (e.g. remote installs, system restores, etc.)

For developers

Hold on to your hats, fellow developers: it’s possible to instantiate .Net and COM objects directly from the Powershell console.

If you’ve never worked with a language that has an interactive console (e.g. Ruby, Python), maybe you haven’t fallen off you chair yet.

I think an interactive console is a great way to explore a language or some of its functionalities, like for instance an API class or method you’ve never used before.

Some examples:

1
2
$ts = new-object System.TimeSpan(16, 05, 0)
$ts

Hmmm, is that really a .Net object? Let’s see:

$ts | Get-Member

Nice, we’ve got all our .Net methods! No more need for a ‘Dummy’ project to fiddle with all those obscure API bits!

Here are a couple more usage examples.

Calling a static method:

$n = [System.DateTime]::Now

Id check:

$n | Get-Member

Using an Enum is similar to accessing a static member:

$d = new-object System.DateTime(1999,12,31,23,59,59, [System.DateTimeKind]::Local)

Instanciating an object with its parameterless constructor:

$st = new-object System.Diagnostics.StackTrace

Powershell is also extensible. It’s possible to create Cmdlet’s (the horrible name they gave Powershell commands) and assign aliases. A Cmdlet would be Get-ChildItem, while its aliases are dir, ls and gci.

Finally, Powershell is powerful enough to automate most project automation tasks, such as automated builds and other continuous integration tasks.

Creating and running your own scripts

To wrap it all up, I feel I need to give a couple of pointers on how to create scripts per se.

Extension

.ps1

What more can I say? :-)

Security

Probably as a consequence of being bit pretty hard with all vbs exploits in the past years, Microsoft made sure that by default, after installing PS you were still as safe as before. It’s always nice not to be instantly vulnerable to a whole new class of exploits when you install new tools.

The first security measure is that by default, ps1 scripts will not be executed just by invoking them by name (as opposed to batch files, for example). If you want to call a script, it’s with the explicit invocation of the interpreter:

powershell.exe your_script.ps1

More importantly, Powershell supports the signing of scripts as well as execution policies. You have 4 choices of execution policies: Restricted (default), AllSigned, RemoteSigned and Unrestricted.

Restricted mode means it won’t run scripts at all. So as any good developer would do:

Set-ExecutionPolicy Unrestricted

Now you can start your scripts from the console.

.\myscript.ps1

Yes, the .\ part is mandatory now. Another security flaw fixed.

If you really want that, of course, you can associate .ps1 to the powershell.exe interpreter, which should be in your path.

Arguments

To top it off, you can access the arguments passed to your script with the $args array.

$args | Foreach-Object { $_ }

Conclusion

I first stumbled upon the very good “Learn Powershell in your Lunchbreak” series, and boy, it’s quite a mouthful! It covers a lot of ground and goes quite deep into a lot of uses for Powershell. A must-read for anyone interested in learning more about Powershell.

My goal with this article was to offer a more gentle introduction to Powershell and maybe even open eyes that were once closed to this new and very powerful tool for the Windows platform. Let me know if I succeeded or failed miserably :-) If you have suggestions, questions or corrections, they are welcome as well.

References

Some Powershell blogs

Comments