PowerShell In GUI Blog

PowerShell GUI Box Just In A Few Clicks

Preventing Problems In Your PowerShell Code

leave a comment »

Last month a wave of using the Set-StrictMode commandlet flooded the PowerShell Internet landscape.

In receiving occasionally a tweet from powershell.com about using it with the -Version Latest parameter, I re-wrote several my old modules, three plus lines each, to meet the requirement. Needless to say that it helped to fix some deep mistakes in code, causing to reduction of code execution. These modules collect information, so that their execution don’t stop, but the data collected are not complete.

The topmost problem was the use of CSharp-like variable checks:

if ($Variable -ne $null){

Owing to a habit of using such a style, I left intact these checks, only outermost try-catch statements were added:

try{$null = $Variable;
if ($Variable -ne $null){

}}catch{}

where catch instruction empty or contains the error-handling function, just depending on a situation. Especialy, if what is meant to be $Variable is something like $Variable.Property.

Bringing the first story to an end, I need to say that my style of error-handling is formed mostly on recommendations given in John Robbins’s book about .NET debugging, the second edition if I recall it right. He insisted on testing return values and input parameters rather than on generating exceptions, so that I picked up this style somewhere in this millennium.

The example above is typical of such an approach: try statement is used as a protection level 1 from error thrown by Set-StrictMode, at the same time, the if statement defends the furthermost code from a useless value of a variable, forming level 2 of protection. In common, it works well, but weights heavy.

Not so much time went out since I finished tayloring mycode to support the Set-StrictMode cmdlet, here came a new issue I must investigate: http://www.windowsitpro.com/blogs/powershell-with-a-purpose/entryid/76307/is-there-an-option-explicit-in-powershell

Don warned on scopes. Personally, someday I had a problem like:

for($i = 0; $i -lt 10; $i++)
{
    #some code
    for($i = 0; $i -lt 10; $i++)
    {
        #some code
    }
}

In this example, the index variable omits several states as a result of being increment in the inner cycle:

for($i = 0; $i -lt 10; $i++)
{
    Write-Host $i;
    for($i = 0; $i -lt 10; $i++)
    {
        Write-Host $i;
    }
}

0

0

1

2

3

4

5

6

7

8

9

The following sample code does the same:

for($private:i = 0; $private:i -lt 10; $private:i++)
{
    Write-Host $private:i;
    for($private:i = 0; $private:i -lt 10; $private:i++)
    {
        Write-Host $private:i;
    }
}

To heal the situation, you need use as many unique names of variables as you can afford. I suppose, you might have tens of cycles in your code. The following example is that you need to cure the preceding one:

for($private:i = 0; $private:i -lt 10; $private:i++)
{
    Write-Host $private:i;
    for($private:i2 = 0; $private:i2 -lt 10; $private:i2++)
    {
        Write-Host $private:i2;
    }
}

There may be offered a dubious solution, that some people used in VBScript programming: to declare ALL the script variables at a begin of every script/module file.

The argument that there is a great memory consumption is not serious due to a usual great memory consumption of many PowerShell scripts callingmodules and gathering some data.

Following the previously mentioned, the protected code is something like:

[int]$private:i = 0;
[int]$private:i2 = 0;
#
#amount of code
#
for($private:i = 0; $private:i -lt 10; $private:i++)
{
    Write-Host $private:i;
    for($private:i2 = 0; $private:i2 -lt 10; $private:i2++)
    {
        Write-Host $private:i2;
    }
}

Along with this ‘scope problem’, there’s another scope problem, namely ’embedded variable problem’. Notice, that variables $_, $Error and several other might contain various values at the execution time.

I saw the code where these variables worked differently on a module load and in time module functions are called. This was because of using Import-Module with the -Force parameter. Global value of $_ can be easily spoilt, so can be spoilt $script:_, but this is observed not so frequently.

The last example of today’s article demonstrates how we can spoil the $global:_ variable. It’s supposed that you have a couple of module *.psm1 files in paths stored in the $PSMOdulePath variable. If you does, let’s see how values of $global:_ and $script:_ are changed:

cls
[int]$counter = 0;
$env:PSModulePath.Split(";") |  %{Get-ChildItem -Path $_ -Include *.psm1 -Recurse | %{Write-Host '1' $_;
        $counter++;
        if ($counter -eq 1)
        {

$module = "${env:temp}\test.psm1"
@'
function Out-Items
{    param([string]$Comment)
    [string]$private:regPath = `
        'Registry::HKCU\Keyboard Layout';
    Get-ChildItem $private:regPath | `
        Select-Object -First 1 | `
        %{Write-Host 'Registry' $Comment '$_ :' $_;
          Write-Host 'Registry' $Comment '$private:_ :' $private:_;
          Write-Host 'Registry' $Comment '$local:_ :' $local:_;
          Write-Host 'Registry' $Comment '$script:_ :' $script:_;
          Write-Host 'Registry' $Comment '$global:_ :' $global:_;};
}
Out-Items 'on loading the module';
Export-ModuleMember -Function Out-Items
'@ > $module

Import-Module $module -Force;            

        }
        Write-Host '2' $_;
        }}

Out-Items "called from the console";

The salvation is in using bracketing with {}.GetNewclosure() method as shown below:

cls
[int]$counter = 0;
$env:PSModulePath.Split(";") |  %{Get-ChildItem -Path $_ -Include *.psm1 -Recurse | %{Write-Host '1' $_;
        $counter++;
        if ($counter -eq 1)
        {

$module = "${env:temp}\test.psm1"
@'
function Out-Items
{    param([string]$Comment)
    [string]$private:regPath = `
        'Registry::HKCU\Keyboard Layout';
    Get-ChildItem $private:regPath | `
        Select-Object -First 1 | `
        %{Write-Host 'Registry' $Comment '$_ :' $_;
          Write-Host 'Registry' $Comment '$private:_ :' $private:_;
          Write-Host 'Registry' $Comment '$local:_ :' $local:_;
          Write-Host 'Registry' $Comment '$script:_ :' $script:_;
          Write-Host 'Registry' $Comment '$global:_ :' $global:_;}.GetNewclosure();
}
Out-Items 'on loading the module';
Export-ModuleMember -Function Out-Items
'@ > $module

Import-Module $module -Force;            

        }
        Write-Host '2' $_;
        }}

Out-Items "called from the console";


Advertisements

Written by Alexander Petrovskiy

April 13, 2011 at 5:17 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: