Posts Tagged ‘Powershell’

Powershell pitfalls

June 21st, 2018
1
2
3
function T ($b){Write-Host "[$b]"; return $b}

if( T($true) -and T($true)){1}else{0}

I would expect the above to return

1
2
3
[True]
[True]
1

but no. Instead

1
2
[True]
1

is returned.

Why?

It’s because a method call in an if statement has to be surrounded with paranthesises like so:

1
if( ( T($true)) -and (T($true)) ){1}else{0}

Now the output is

1
2
3
[True]
[True]
1

 

Powershell for devs, part 2

April 28th, 2018

Continuing from Part1.

New to Powershell? Let me jot down a few good-to-know things. Some you already know. Some might save you hours of googling.

Below is a simple module. I will explain it row(s) by row(s).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Set-StrictMode -Version 2.0

< #
.SYNOPSIS
Short description.

.DESCRIPTION
Long description.

.PARAMETER name
This does not show with Get-Help.

.PARAMETER birthDate
This does not show with Get-Help.

.EXAMPLE
An example...

.NOTES
General notes.
#>
function Get-PersonData
{
    param(
        [parameter(
            Mandatory=$true,
            HelpMessage='The name of the culprit.'
        )]
        [string] $name,
        [datetime] $birthDate = (Get-Date)
    )
    $startTime = Get-Date -Format 't'
    Write-Verbose "Start:$startTime"
   
    "Name=$name, Date=$($birthDate.ToString('yyyyMMdd'))."

    # Call method | Filter | Sort | Return.
    $foundPerson = GetPeople `
        | where {$_.name -eq $name } `
        | Sort-Object born `
        | Select-Object -First 1

    Write-Host "foundPerson:[$foundPerson]"
   
    Write-Verbose "Stop:$(Get-Date -Format 't')"

    return $foundPerson
}

function GetPeople(){
    # Create array of key-value pairs.
    $people =
        @{name='ola';born=[DateTime]'1970-10-13';children=1},
        @{name='anders';born=[DateTime]'2011-01-01'}

    return $people
}

Export-ModuleMember Get-PersonData
# Export-ModuleMember GetPeople
1
Set-StrictMode

Use Set-StrictMode. See part1 of this blog series.

1
# .SYNOPSIS... #

This type of comment right before a method is recognised by Powershell and ends up in Get-Help.  Adhering to explaining the intention for your methods is considered good practice.

Inside the method is

1
2
3
4
5
6
7
8
param(
    [parameter(
        Mandatory=$true,
        HelpMessage='The name of the culprit.'
    )]
    [string]$name,
    [datetime]$birthDate=(Get-Date)
)

This is what the parameters look like. One can set if a parameter is mandatory, some help text to be picked up by your favourite text editor, the [type] and default value.

1
$startTime=Get-Date-Format 't'

A variable is set to a string. Other scripting languages send strings around. Powershell sends proper objects. The Get-Date-Format converts the DateTime value to a string.

1
Write-Verbose "Start$startTime"

Built into Powershell is the possibility to call (almost) anything with a -Verbose flag. Only then is the Write-Verbose called. Like a simple logging level.

1
"Name=$name, Date=$($birthDate.ToString('yyyyMMdd'))."

Nothing strange here at first sight. Until you exeute in a console. Then you realise this string is outputed; because it is not inputed into something else, like a variable.

Also “$($variable.Method)” is the way to call methods inside a string.

1
2
3
4
$foundPerson= GetPeople `
    | where {$_.name-eq$name } `
    | Sort-Object born `
    | Select-Object-First 1

Powershell is said to be able to use Linq. This is technically true but the syntax is so weird that I have never used it. This code though has (almost) the same behaviour and is easy to read.

GetPeople is a method call. Backtick concatenate lines and circumvents that Powershell has automatic statement ending with a line end. | pipes object and not strings. where is an alias for Where-Object. The rest is… Linqish.

1
Write-Host "foundPerson:[$foundPerson]"

Write-Host an object like $foundPerson output the contents of the object. Not just the type as in C#.
Write-Verbose “Stop:$(Get-Date-Format ‘t’)”
This string is outputed only if the method call is made with -Verbose. Plus an exmple on how to write a method call in a string.

1
return $foundPerson

Finally nothing surprising. Except that return can be left out to make the code harder to read.

1
2
3
$people=
@{name='ola';born=[DateTime]'1970-10-13';children=1},
@{name='anders';born=[DateTime]'2011-01-01'}

@ tells Powershell to create a list of key-value pairs. Also often called a hash list.

Note the comma character. That makes $people an array of key-value pairs.

1
Export-ModuleMember Get-PersonData

Only exported mehods are visible outside the module. It is like making them public.

Powershell for devs, part 1

April 28th, 2018

New to Powershell? Let me jot down a few good-to-know things. Some you already know. Some might save you hours of googling.

Use Set-StrictMode -Version 2.0.

Powershell 5.1 is the last Windows Powershell. From 6 it runs on Dotnet core and more platforms.

Use Pester for automatic testing. It runs tests and can mock. Note that mocking works differently in Powershell than C# as they load code in different ways.

Don’t patch together your Powershell scripts. Use the systems thinking you usally do and create a sturdy, thought out, solution with bricks of the right size. Just like you would any other solution.

For caveats let me explain the file below, row by row.

File JustASimpleScript.ps1

1
2
3
4
5
6
7
8
9
10
11
12
Set-StrictMode -Version 2.0

$temp

$temp = 'LocalVariable'

function LocalFunction( $foo, $bar ){
    Write-Host "Local variable is $temp."
    $foo    # The last executed row is returned.
}

LocalFunction 'MyParameter'

Enter the above in your favourite text editor. Save it as JustASimpleScript.ps1. Open a console and navigate to the proper folder. Execute Powershell to get Powershell started. Then execute .\JustASimpleScript.ps1′. The result shows both an exception and some more proper output.

1
Set-StrictMode -Version 2.0

Set-StrictMode is like `option explicit` in old VB6, it throws an error if you try to evaluate a variable that has not been set, that parenthesises are not used when calling functions and called methods do exist.
No code example at Stackoverflow shows it and almost no blog article.

1
$temp

Variables are recognised by the leading dollar sign.

As we used Set-StrictMode above this row throws an exception. But… the program continues to run!

1
$temp = 'LocalVariable'

Strings are delimited with apostrophes. Quotation works but is over kill, see below.

1
function LocalFunction( $foo, $bar ){

Nothing special about declaring a function like this. Declare a function with parenthesises but call it without; it is very easy to get this wrong. Also; the parameters are optional by default.

1
Write-Host "Local variable is $temp."

Write-Host is the normal way of outputting text in the console. If it is the correct way is another discussion I won’t dive into without more knowledge.

Also note the quotation marks. They mean that anything that looks like a variable inside should be evaluated. Just like PHP. Just like C# by prefixing a string with $. Many online examples use quotation marks for every string. By the time of writing I consider that less good.

1
$foo    # The last executed row is returned.

To fool newbies, Powershell implicitly returns the last executed row with an output. I suggest to make it more readable, prefix with return like so:

1
return $foo    # The last executed row is returned.

Comments starts with a # sign.

1
{...}

Stuff within curly brackets are a code block. A code block is not only the contents of a method or an if statement but can also be passed around, like a() => {…} lambda in C#.

1
LocalFunction 'MyParameter'

This ia a method call. As long as it is Powershell leave out the parenthesises. Otherwise you have converted your list of parameters to a single parameter call and the single parameters is a list. This rule is not hard to remember but reading code that calls method with lists as parameters is hard to grasp for a newbie. Adding insult to injury, calling a C# method from Powershell might change the rule.

Using a file like this, ending in ps1 is typically done by “dot sourcing”. It is quick, dirty and pollutes the global name space. Things you would never to in your “regular language”.

A call like below makes the contents live only just in the call.

1
.\JustASimpleScript.ps1

If you want to make the $temp variable and the LocalFunction live on you start it with yet a period and a space like so:

1
. .\JustASimpleScript.ps1

But probably you want to make a module instead. Which you find in tags: | categories: Code and Development | no comments »