Use PowerShell
The Shell Is Calling
The Shell Is Calling
Sep 13th
I’ll get the slides posted sometime (in the middle of migrating my machine to the developer preview of Win 8), but the links I mentioned are:
—Project Home – http://www.show-ui.com
—Codeplex Site – http://showui.codeplex.com
—Doug Finke’s Blog – http://dougfinke.com/blog
Aug 15th
My friend Robert Robelo was kind enough to point out a problem with the proxies created by the CommandAssist module.
In trying to research the problem, I found that PowerShell crashed ( yes, crashed.. not throwing an error, but CRASHED) when Get-Help could not resolve the command it needed to provide help for.
Create a module that exports a function
$Path = 'c:\users\smurawski\documents\windowspowershell\TestModule.psm1'
$null = new-item $Path -ItemType File -Force -Value @'
function Test-One {
[CmdletBinding()]
param($x)
$x
}
Export-ModuleMember -Function Test-One
'@
Import the module
Import-Module TestModule
Create the proxy module
Get-Module TestModule | New-AssistedModule -ModulePath c:\users\smurawski\documents\windowspowershell\TestAssisted
Import the proxy module
Import-Module TestAssisted
Try to Get-Help for Test-One (shell will crash here)
Get-Help test-one
In the proxy command that is generated, there is a help item (ForwardHelpTargetName) that is created to point the help file back to the original command. Since you could possibly have multiple commands, aliases, and functions with the same name, there is a second help entry (ForwardHelpCategory) that allows you to specify what type of command you are forwarding to.
NOTE: The following is my opinion of what is happening based on my testing. I haven’t attached a debugger and watched to verify, but I have tested enough to confirm a workaround. This occurs in both the ISE and PowerShell.exe. I have not tested in other scripting environments.
In this case, both the original command and the proxy are functions, so Get-Help goes into a loop until it hits a recursion max and crashes PowerShell.
So, how can we get around this? CommandAssist relies on pointing back to the source command for the help files.
Fully qualifying the command in the proxy will solve the problem. I’ve updated the proxy command generation to qualify the command in the ForwardHelpTargetName with the module prefixed name of the source command.
If you’ve been playing with CommandAssist, you will want to regenerate your proxies.
I don’t know about you, but I would like to see this type of error not crash PowerShell. I’ve filed a Connect bug and would appreciate if you would head over and vote it up! The team really does respond to community feedback and Connect is a great way to show that this is important to you!
May 18th
$x and $z and $foo just aren’t going to cut it in your production scripts.
Andy Schneider tweeted during the Scripting Games that there was no penalty for providing a descriptive variable name. He was right and not quite right enough. Twitter does not provide you the opportunity to share more than a sound bite in one tweet, so he really could not have commented much further there, but he did in a post that expanded on what judges were looking for in style for the Scripting Games.
The content of that post is not limited to the Scripting Games, but should be applied to our scripting experience.
We’re going to focus on one particular section of that blog post.
Use variables that make it easy to understand what you are doing in your script. Comments in code are great, but your code should be readable and understood without them. In general, I would choose good variable names and clear processes over heavily commented code.
If you find yourself working on a script or function and begin to think, "I need a comment here to make sure it is clear what I’m doing," then you are already headed for trouble. (Function, cmdlet, and script names are another portion for another day.)
Descriptive variable names can serve as inline documentation on what that variable represents. A descriptive variable makes it much easier to follow that variable through the script.
Remember that you might not be (and will likely not be) the last person to look at that script and need to figure out what is going on internally.
If by chance you are the last person to look at that script, do you think you are going to be able to have the same contextual understanding one month after you wrote the script? Two months? Six months or more? I know that when I review scripts I wrote years ago, before I followed my own advice better, it can take me a while to figure out what is really going on, even in some medium complexity scripts (where I should really just be able to scan and understand).
Comments are not the workaround for poor variable naming. Scripts are rarely static entities. As requirements change, scripts are updated, sections of functionality are changed and the objects that are operated on can change as well. Comments are often the last thing to be reviewed (if at all), so the likelihood of ending up with stale and misleading comments goes up with each revision.
Another reason that comments aren’t good enough is that they clutter up the script file. Comment-based help is one thing (and that is localized to one place in the file, not spread across the script. When I’m reading a script and there are lines upon lines of comments, it really gets in the way of understanding what is happening in the script. PowerShell is rather unique in the case, since the syntax actually lends itself well to self-documenting code (with how the command naming works).
These are some of the guidelines I try to follow when naming variables.
I’d like to hear what guidelines you’ve followed or what you thing of mine, so post a comment!
May 17th
Glenn Sizemore wrote a great Scripting Games wrap up post about some of the parameter validation options that are available. Go read it, I’ll wait.
Very often you need to make sure that a parameter will not have a null value. There are a couple of options in making sure you have valid content.
When you start to look at some of the validation parameters (in about_Advanced_Functions), the two that leap out are the ValidateNotNull and ValidateNotNullOrEmpty.
Just looking at that names hints that there is something different about them, but it might not be immediately obvious.
ValidateNotNull will check if a parameter value is null when it is assigned. If it has a null value, the runtime will throw an error.
ValidateNotNullOrEmpty has some additional checks to help with arrays and strings. For example, if my function takes a parameter called ComputerName (like
param( [parameter()] [string[]] $ComputerName )
and I apply the ValidateNotNull parameter,
param( [parameter()] [string[]] [ValidateNotNull] $ComputerName )
I will only get an error if I pass $null
c:\scripts\MyScript.ps1 -ComputerName $null MyScript.ps1 : Cannot validate argument on parameter 'ComputerName'. The argument is null. Supply a non-null argument and try the command again.
But, if I pass an empty array, or an empty string, the command will accept the input.
c:\scripts\MyScript.ps1 -ComputerName ""
c:\scripts\MyScript.ps1 -ComputerName @()
To use validation to protect against those types of input, I can use ValidateNotNullOrEmpty
param( [parameter()] [string[]] [ValidateNotNullOrEmpty] $ComputerName )
Then I will receive an error like this.
MyScript.ps1 : Cannot validate argument on parameter 'test'. The argument is null, empty, or an element of the argument collection contains a null value. Supply a collection that does not contain any null values and then try the command again.
Neither ValidateNotNull nor ValidateNotNullOrEmpty force a parameter to be declared. The validations only have an impact if values (or lack thereof) are actually declared or passed in via the pipeline. If you would like to make a parameter required, you can set the Mandatory flag in the Parameter attribute.
param( [parameter(Mandatory=$true)] [string[]] $ComputerName )
This will make the parameter required and does protect against both empty strings and empty arrays, so you will not need to use either of those validation attributes.
NOTE: Other validations can still be done (like ValidateSet, ValidateRange, and ValidateScript).