by: Helge, published: Nov 5, 2014, updated: Dec 17, 2022, in

What I Hate About PowerShell

With all the love PowerShell is getting these days one might think that the golden age of (Windows) scripting has finally begun. Maybe it has. But one man’s nirvana can be another man’s hell.

My Background

I started with programming at the age of 14 by learning GFA Basic on the Atari ST. After devouring the Kernighan and Ritchie book I quickly switched to C and I must say that understanding pointers at an early age really helps in later life!

Go away or I will replace you with a very small script

After a 10 year hiatus I returned my attentions to the world of computing, learning (and loving!) Perl which I mainly used for systems management and migration tasks. I wrote tools like SetACL (in C++), dabbled in Visual Basic (at the time not based on .NET yet), dived into the arcane syntax of Windows batch files and picked up regular expressions along the way. Later I wrote a couple of tools in C# and VB.NET (based on the .NET framwork), e.g. SetACL Studio. For my latest product, uberAgent, I went back to C++.

I am mentioning all of this so that when you decide that I am an idiot after reading this article you at least give me credit for being an idiot who has seen a bit of the programming world.

From Monad to PowerShell

I heard about PowerShell first when it was still called Monad. At the time I was thrilled by the prospect of having a powerful scripting language for Windows. I was hoping for something like VBScript turned good or Perl being included in the default install of Windows. My enthusiasm was such that I wrote a paper explaining PowerShell and presented it to colleagues and customers.

However, over time my enthusiasm waned. I found more and more quirks in the language that made life unnecessarily difficult. Certain things that should be simple and intuitive are not. Perl’s slogan Easy things should be easy and hard things should be possible does not apply to PowerShell.

The remainder of this post is a list of things that, in my humble opinion, are wrong with PowerShell. Some of the points may be subjective. I might be wrong about a thing or two (in which case please correct me by commenting and I will fix the error). You have been warned.

Syntax

Operators

PowerShell’s operators are horrible. E.g. testing strings for equality:

"abc" -eq "abc"

Instead that should be something like:

"abc" == "abc"

The same applies to most other operators. E.g. comparing two numbers:

8 -gt 6

Instead of:

8 > 6

Escape Character

Having the backtick as escape character is just plain weird. Chances are if you have used other programming languages the backslash feels much more natural.

"Let's print line one`nand line two`n and a quote `""

Instead of:

"Let's print line one\nand line two\n and a quote \""

If Requires Curly Brackets

Many languages let you do away with the curly brackets if you have only a single statement in an if. Not so PowerShell. Instead of this:

if (5 -gt 3)
   "hello"

You need to write this:

if (5 -gt 3)
{
   "hello"
}

Function Definitions

Functions can only be used after they have been defined. This leads to constructions like this one where the script effectively starts at the end:

function main
{
   # do stuff
}

# Call main
main

Function Calls

PowerShell functions are called without parentheses, but .NET functions are called with parentheses.

E.g. calling a self-defined PowerShell function LogMessage:

LogMessage "Here's a message"

Versus calling a .NET function

[string]::IsNullOrEmpty($configFile)

Script Syntax Check

Scripts cannot be syntax-checked before they are run. If you have a syntax error in a code branch that is not executed during testing you might only find out about it after the script has been deployed to production machines.

E.g. PowerShell happily executes the following code without the slightest warning:

Set-StrictMode -Version 2

$variable = 10

if ($variable -eq 30)
{
   aslkfjlsjdf     # It probably should complain about this
}

Lack of Mandatory Variable Declarations

This (along with the next point) is by far the worst I have found while working with PowerShell.

If you want to write production-quality code you need a way to force the language to force you to declare variables before they are used. Perl has its use strict, heck, even VBScript has option explicit. Only PowerShell has nothing.

Take a look at the following code. You expect it to print “20”, but because of a stupid typo it prints “10”:

Set-StrictMode -Version 2

$variable = 10

# other code

$varable = 20   # Notice the typo

# print value
$variable

There is a Connect item on this.

Scope of Variables

This is where it gets really funky.

PowerShell uses dynamic scoping with copy on write (versus lexical scoping, which is what everybody else uses). If you have used other languages before this is totally confusing. Even if you have not this easily leads to errors that are really hard to find.

So what is this about? When a new scope is created (basically whenever you declare a function), all variables from the parent scope are copied into the new child scope. Notice the word copied. That means you now have two variables of the same name. When using the variable name in the child scope the child scope’s copy of the variable is changed. Once you leave that scope the change is gone – the parent scope’s copy of the variable remains unchanged.

Take a look at this script:

Set-StrictMode -Version 2

function ChangeVariable()
{
   $variable = 20
   
   # Print the value from inside the function
   "Value from INSIDE the function: " + $variable
}

# Set initial value
$variable = 10

# Print the value
"Value from OUTSIDE the function: " + $variable

# Call the function that supposedly changes the variable's value
ChangeVariable

# Print the value again
"Value from OUTSIDE the function: " + $variable

The value of $variable is printed three times. You might naively assume that the output is “10” followed by “20” and another “20”. Not quite:

Value from OUTSIDE the function: 10
Value from INSIDE the function: 20
Value from OUTSIDE the function: 10

Here is a nice blog post explaining this in more detail.

Previous Article How Folder Redirection Impacts UX & Breaks Applications
Next Article Vendors: Why We Do Not Need Your PowerShell SDKs