What I absolutely hate about Powershell
Powershell in all of its wisdom has some really bad behaviors. Perhaps it’s to make scripting easier for non-programmers. I would argue that these behaviors are worse for those less-experienced because they will cause bugs that are not obvious. Even experienced programmmers would say what the heck! Perhaps there is a legitimate reason for these behaviors. As a person who loves programming languages, I cannot make any sense of it. Here are a few examples.
-
Reading files
-
Problem: when reading text files using the Get-Content command-let, the return type changes depending on the content. If the content has only one line, the return type is a string. However, if the content has more than one line, the return type is an array. You should always want to expect the same return type.
-
Here I have two text files:
1_line.txt
hello world2_lines.txt
hello world Brian! -
Here are the return types for each file using Get-Content:
1 line in file, return String
$data = (Get-Content .\1_line.txt) Write-Host $data.GetType()output
System.StringMore than 1 line, return Object Array
$data = (Get-Content .\2_lines.txt) write-host $data.GetType()output
System.Object[]
-
-
Solution 1: always force the file to read as a string and then split on the newlines
$data = (Get-Content .\1_line.txt -Raw) -split '`n` Write-Host $data.GetType()output
System.String[] -
Solution 2: use .NET classes to read file lines (requires that
$ExecutionContext.SessionState.LanguageMode -ne "ConstrainedLanguage")$data = [System.IO.File]::ReadAllLines(".\1_line.txt") Write-Host $data.GetType()output
System.String[]
-
-
Block scope
-
Problem: Powershell does not have block scope. It has Global, Local, and Script scope. Powershell also has scope modifiers. Since there is no block scope, you can reference a variable after the block executed:
if ($true) { $ghost = "ghost should be out of scope" } Write-Host $ghost.Trim()output
ghost should be out of scope- However, if the block does not execute, the variable reference can fail:
if ($false) { $ghost2 = "ghost should be out of scope" } Write-Host $ghost2.Trim()output
You cannot call a method on a null-valued expression. At line:1 char:9 + Write-Host $ghost2.Trim() + ~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull- Again, the block does not run, so
ghostdoes not exist outside the block. This behavior should always be expected outside of the block.
-
Unfortunately, other dynamically typed, interpreted laguages such as Python, Ruby, and Javascript behave in this way too. Javascript does support block scope when using
letinstead ofvar
{ var some_var = 5; } console.log(some_var); 5{ let some_other_var = 10; } console.log(some_other_var); Uncaught ReferenceError: some_other_var is not defined VM190:1 at <anonymous>:1:1 (anonymous) @ VM190:1 -