June 7, 2026 • 2 min read • 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 are two text files and their content:
1_line.txt
hello world
2_lines.txt
hello world
Brian!
Here are the return types for each file using Get-Content:
1_line.txt - one line in file, returns String
$data = (Get-Content .\1_line.txt)
Write-Host $data.GetType()
System.String
2_lines.txt - more than 1 line, returns Object Array
$data = (Get-Content .\2_lines.txt)
write-host $data.GetType()
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()
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()
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()
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()
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 ghost does 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 let instead of var
{
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 :1:1
(anonymous) @ VM190:1