Sunday, May 31, 2009

Using Strict Mode with Set-PSDebug

If you've used Perl in the past, then you're probably familiar with "use strict" (if you've been using Perl without "use strict", then head over here Note: the scoping issues don't apply to PowerShell).

By default, PowerShell is very accommodating. If you tell it to echo the value of $chouderhead, it will happily echo... NOTHING! There is no variable called $chouderhead, you misspelled $chowderhead. PowerShell doesn't like to make waves, though, so it happily creates a new variable named $chouderhead and starts using it. This can lead to very hard-to-debug issues.

You can fix this By adding this line to your profile (or just typing it in at the prompt):

PS> Set-PSDebug -strict

Now if you try to use a variable that hasn't yet had a value assigned to it, you'll get an error like this:

PS> $chouderhead
The variable '$chouderhead' cannot be retrieved because it has not been set.
At line:1 char:13
+ $chouderhead <<<<
+ CategoryInfo : InvalidOperation: (chouderhead:Token) [], RuntimeException
+ FullyQualifiedErrorId : VariableIsUndefined

The error is annoying, but if you've ever wasted a half an hour wondering why your script isn't outputting what you thought it would, only to realize that on line 304 you accidentally used an 'm' instead of an 'n' in your variable name and how could I miss that, it's so stupid, and now I missed dinner, and my back hurts from crouching over my keyboard staring at the screen for too long and curse the day I was born! Why, oh, why didn't I just add 'PSDebug -strict' to my profile? Why?

But I digress...

Tuesday, May 19, 2009

Get-ChildItemRecurse Update

Someone named CrazyDave made an important update to the script I made yesterday. I was checking for the specific type System.IO.DirectoryInfo. The problem with this is, it unnecessarily limits my script to being used on files and folders. Get-ChildItem makes no such distinction. It can be used on any PSDrive.

So here is the corrected version: http://poshcode.org/1115 (highlighted line is the old one)



Monday, May 18, 2009

A Get-ChildItem Wrapper That Lets You Say How Deep to Recurse

I noticed that a couple of people hit my blog that seemed to be looking for a script like this, and it turned out to be an interesting exercise.

Notice that I took the fileglob and moved it down the script rather than just using it in my call to Get-ChildItem. I had to do that because otherwise I would only have been recursing through directories that matched the pattern, and I wanted to recurse down into all directories, but only send those entries that match the pattern down the pipeline.

The function is here, for those that don't see it inline in the post: http://poshcode.org/1113



Sunday, May 3, 2009

Use PowerShell to Recursively Get Group Members

I've noticed looking at my StatCounter logs that a few people found my post on recursion while looking for a way to recursively get group members in PowerShell, so I whipped up this script.

Note that it depends on the AD Cmdlets from Quest, and that it works in much the same way as the example. Whenever it finds that one of the group members is a group, it calls itself on that group and adds the result to the list of users.

Also note the technique of using a hashtable to keep a list of unique users. It's common to store a list in an array and use use -contains and -notcontains to find out if an item already exists, but this is tremendously inefficient compared to using a hashtable as an index, especially as the list of users gets longer, because it has to keep cycling through the list over and over. It's a trick I picked up in Perl that works equally well here.



Saturday, May 2, 2009

An Excellent Post on Bitwise Operators

If you find yourself working with Microsoft APIs for any length of time, you'll find yourself having to deal with bitmasks and bitwise operations. Mark Schill over at String Theory has an excellent description of what they are and how to work with them.



Friday, May 1, 2009

Advanced Functions: Using Values from the Pipeline (2.0 CTP3)

Note: The following code will only work on Powershell 2.0 CTP3 or later.

A while back I showed how you can use $input as a parameter to a function in order to use it in the pipeline. Unfortunately that technique has the side effect of making PowerShell stop and gather up all variables that are being sent down the pipeline into $Input and then passing it to the function. Ordinarily it doesn't matter much, but if you are processing a lot of data, say the results of a SQL query, you may end up using up massive amounts of memory and watch your computer grind to a halt while the memory manager swaps a billion times a second (okay, I made that number up).

The ValueFromPipeline Parameter Property

As the name suggests, this property indicates whether or not a parameter can take a value from the pipeline. Just set it to $true when declaring the variable (you'll see an example at the bottom of this post).

Doing that alone will not give you the effect you're looking for, though. It will mysteriously process the first item passed down the pipeline...and then suddenly stop. This was very frustrating when I first came across it, and left me scratching my head, until a post on the microsoft.public.windows.powershell group happened to mention the critical missing piece in an unrelated discussion.


BEGIN, END, and PROCESS Blocks

The first thing you need to know is that if you want to take a value from the pipeline and use it, then you need to add in the optional PROCESS Block. Every function essentially has a BEGIN, END, and PROCESS scriptblock in it:
  • BEGIN - This scriptblock is run the first time the function is launched
  • PROCESS - This scriptblock is run each time the function receives input
  • END - This scriptblock is run after all input has been processed
Here's the important part: explicitly declaring these blocks is optional, but if you don't use them, then the body of your function executes in the END Block. That's why it was only executing once.

As an example of a script that uses these blocks, take a look at this little ditty I whipped up for work (I highlighted the important parts):