First let me explain something: I LOVE POWERSHELL...
No, I really do. After 3 years working every day with batches, vbscript & .NET I can see huge huge potential in PowerShell and more and more I use it I think it is genius shell.
However I am always trying to be skeptical about everything I do - and about every product I love.
I don't want to end up like some people that think their favorite product is PERFECT - and when there is new version, they suddenly realized that something that was PERFECT is more PERFECT ;)
I worked with Monad\PowerShell a lot 3 years ago, then I stopped and returned to batches (because of my work). It was quite fun and it turned out that with good imagination you can still achieve a lot with batches (script blocks, complex scopes, private variables, subroutines, functions etc ;)), however it was always just about building some workarounds. 1 month ago I returned to PowerShell and now I am working with it on daily basis - and of course I already encountered some things I don't really like.
Working with function output
If you are familiar with programming, following code should be easy to understand for you:
Function bar {
$MyVariable = "Foo"
Return $MyVariable
}
Function bar returns MyVariable. So using $X = bar should be equivalent to $X = $MyVariable in fact.
Not in PowerShell. $X will get output from WHOLE function. That means that following function is equivalent to one above:
Function bar {
$MyVariable = "Foo"
$MyVariable
Return
}
I really, really don't like this behavior. Creating complex scripts is getting much more complicated. Consider example where you run into some problems in bar function. Most primitive debugging is always to use Echo (Write-Host) and just see value itself.
If I would have enough time, I would change it as follows:
Function bar {
$MyVariable = "Foo"
Write-Host "DEBUG: $MyVariable"
Return $MyVariable
}
Result would be as expected - if I will use $X = bar, $X will be Foo. However what if I try following:
Function bar {
$MyVariable = "Foo"
"DEBUG: $MyVariable"
Return $MyVariable
}
Looks fine? But doesn't work - $X in this case is Foo Foo and you will never see "DEBUG: Foo" output in console. I really think this is against object-oriented principle of PowerShell and it reminds me of 'For /f' behavior in batches.
You probably won't run into this problem - however you will run into it if you will start creating really complex scripts with XML handling, adding new elements etc. Usually (in .NET), if you for example add something to array, index is returned.
Look at following function. Variable $MyVariable is created, two numbers are added (1 and 2) and then it is returned:
Function bar {
[System.Collections.ArrayList]$MyVariable = @()
$MyVariable.Add("a")
$MyVariable.Add("b")
Return $MyVariable
}
Normally you would expect to get array that contains 'a' and 'b'. But because of PowerShell way of returning object, instead you will get array with 4 (!) entries: 0 1 a b.
0 and 1 in this case is index of added array elements.
To make this function work as expected, you will need to redirect output to null:
Function bar {
[System.Collections.ArrayList]$MyVariable = @()
$MyVariable.Add("a") | Out-Null
$MyVariable.Add("b") | Out-Null
Return $MyVariable
}
Usually I try to write all my functions in following format:
Function <Name> {
<initial checks>
...
<final checks before return>
Return
}
With PowerShell this is usually not possible, because even if you check if $MyVariable is correct type with correct values etc, it is quite hard to know if something "leaked" before.
For me ideal solution would be that PowerShell would support BOTH methods for returning:
Function bar {
$MyVariable = "Foo"
Return $MyVariable
}
Would return object $MyVariable
Function bar {
$MyVariable = "Foo"
$MyVariable
Return
}
Would return whole output from function