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:
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
|
|
Strings
1 2 |
|
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
|
|
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
|
|
Being verbose (you, that is)
1
|
|
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 |
|
“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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
File formats
A couple of nice file formats are supported by default.
txt
Of course!
1 2 3 |
|
Note: Out-File
is just for when you need to use the command’s additional parameters.
csv
1 2 |
|
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 |
|
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
- Get Powershell
- Microsoft’s Powershell quick reference
- Learn Powershell in your lunchbreak: Day 1: Getting Organized Day 2: Writing Scripts and Translating VBScript Day 3: Discovering objects (COM, WMI & ADSI) Day 4: Ins and Outs of the Windows Registry Day 5: Using WMI Day 6: ADSI Connecting to Domains/Computers and Binding to Objects Day 7: Manage Users Note: you’ll need long lunch breaks :-)
- Explorer shell extension Powershell Prompt Here, by Scott Hanselman
- PowerGUI, a Powershell editor with code completion and syntax highlighting.
- The idea for multiline comments came from this article.
- Look over there as well, you’ll find a lot more information ;-)
Some Powershell blogs
- The Powershell team’s blog
- Dmitry’s PowerBlog: PowerShell and beyond
- Posholic
- MSGoodies
- Note: I made a Yahoo Pipe for this blog that follows only the stuff tagged with Powershell
- Keith Hill’s Blog: PowerShell