don't even get me started on PS.

try this PS script out

clear
function Get-Strings1 {
@('hello')
}

function Get-Strings2 {
@('hello', 'world!')
}

(Get-Strings1).Length
(Get-Strings2).Length

OUTPUT:

5
2

You see, PS will strip away the array for 1-element arrays. (Get-Strings1).Length is 5 because you're really calling Length on the string and not the array (the array doesn't exist at that point). As you can see with (Get-Strings2).Length, if there is more than 1 element it will not do this.

and you'll watch different features crash into each other in bizarre ways. For example, everything is an object but if that's the case how do you output log statements to the console since it's also a shell?

PS is hosted, the default PS host will print out whatever reaches the "top". Think of your PS code as the "first function" with it's own set of returns. The host will print out whatever your code "returns", but this is consistent all the way down the chain.

clear
function Get-Strings3 {
(Do-Stuff)
return 'hello'
}

function Do-Stuff {
Write-Output 'haha'
}

(Get-Strings3).Length

OUTPUT:

2

So your returns can be polluted by downstream function calls, including calls into packages that you don't easily have access to the source for. So you'll eventually start writing defensively.

(Do-Stuff) | Out-Null # throws away anything coming out of the function call

And consider what we've demonstrated here. That PS will happily remove arrays based upon runtime state and happily add them based upon runtime state.

If you're confused about why this still happens even though this version uses the return keyword, remember it's PS which means any silly, preconceived notions you have are out the window.

returns does two things.

1. Write-Output

2. Ends execution of the function

If you remove the return keyword it will have the exact behavior it did previously because it's the last statement in the function. And yes, that's how easy it is to accidentally write something to the output and thus turn your return value into an array.

Since I'm on a roll, 1 last example.

clear

$arr = @($null, 'hello')

$isNull = $arr -eq $null

$isNull.GetType()

OUTPUT:

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array

How is the equality operator (-eq) returning an array?

You see, PS has been lying to you all this time, arrays in PS aren't arrays. They're wrapper objects that pretend to be an array to give all sorts of cool magic. One of those magic things being it's propensity to forward function calls on the array wrapper to the elements themselves.

What it's actually doing is calling -eq on each element (think LINQ select) and returning a new array.

Which is useful, you can imagine having a list of COM objects with a name property, you can do a select on the name by just doing ".name" on the array wrapper itself. It has its uses.

BUT

you can't do that for any properties that are on the array (such as Length). Also, PS allows you to attach functions to objects at runtime, if you were truly evil you could attach a myriad of functions to an array object you handed back and laugh as the poor suckers using your code can't understand why calling .Name doesn't forward like it should.

----

I could go on and on and on about PS. I once built a VPS automation solution in PS. My (naive) thinking at the time was that the remoting capabilities for windows was useful so I may as well build it out in powershell. I maintained that solution for 5 years, 4.5 years in and I was _still_ getting surprised by interactions w/i PS.

Here's an exercise for the reader.

write a function that will successfully test an input parameter against null for everything. There's a solution.