C# to Powershell: Part2

Welcome to Part 2 of my C# to PowerShell series where I talk about interesting things I’ve learned on my path from C# to PowerShell. This article will focus on Casing, Error Handling and some of the minor oddities that PowerShell has.

Case Sensitivity

Ok, I admit, the graphic above is probably overkill on the scary chart, case sensitivity isn’t that big of a deal in PowerShell. By default, all variables, functions, and comparisons are case-insensitive. Basically the polar opposite of everything in C#. Fun right!

Variables

All variables are case-insensitive, this is a hard one to adapt to as a C# guy.

$test = "My name is Tim"
$Test
$test
$teST

All of the variables listed equal “My name is Tim”. Now, this may not seem like a big deal because honestly, who used thisVar and ThisVar in the same place anyways? I can think of some rare occasions where it felt necessary, but it was probably wrong anyways.

Comparison

This is where things get fun. In C# comparing two strings was made trickier if casing was different. A common quick approach is to use ToLower() to force both strings to lowercase for a comparison, or get fancy and use the Equals("test", StringComparison.OrdinalIgnoreCase), but who can remember that?

In PowerShell it’s much easier.

"Test" -eq "test"
True

Yep, that equals True. By default comparison is performed case-insensitive. So what do you do if you want to perform a case-sensitive comparison, only a crazy person does that right?

"Test" -ceq "test"
False

Adding a ‘c’ in front of the ‘eq’ turns case-insensitive equals in to case-sensitive equals. This works for all the comparison operators, for example -like and -clike.

"Test" -like "test"
True
"Test" -clike "test"
False

The weird one nobody talks about

Surprisingly there is a way to explicitly force case-insensitivity. The same comparison operators that can have a ‘c’ added to force case-sensitivity can also have an ‘i’ added to force case-insensitivity.

Like me you are probably asking yourself why. I’ve asked around and I haven’t found a good answer yet, and I’ve never seen it in the wild. You now know it exists though!

InsensitiveSensitiveExplicit Insensitive
-eq-ceq-ieq
-ne-cne-ine
-gt-cgt-igt
-ge-cge-ige
-lt-clt-ilt
-le-cle-ile
-like-clike-ilike
-notlike-cnotlike-inotlike
-match-cmatch-imatch
-notmatch-cnotmatch-inotmatch
-contains-ccontains-icontains
-notcontains-cnotcontains-inotcontains
-in-cin-iin
-notin-cnotin-inotin
-replace-creplace-ireplace
Comparison Operators Casing

When it doesn’t

Ok, here is the part that contains the scary red text from above. When isn’t PowerShell case-insensitive?

Methods

Certain Methods are case-sensitive, for example the .Contains() method, what makes it even more fun is the -contains operator being case-insensitive. Another common one is .Replace() and -replace

$test = "One","Two","Three"

# Contains Operator
$test -contains "one"
True
$test -contains "One"
True

# Contains Method
$test.Contains("one")
False
$test.Contains("One")
True

Escape Characters

Escape Characters only activate when lowercase.

  • `0 Null
  • `a Alert bell/beep
  • `b Backspace
  • `f Form feed
  • `n New line
  • `r Carriage return
  • `r`n Carriage return + New line
  • `t Horizontal Tab
  • `v Vertical Tab
"I love `nPowerShell"
I love
PowerShell
"I love `NPowerShell"
I love NPowerShell

talk about Error handling try/catch

Third-Party Commands

When it comes to commands not built in to the core of PowerShell care should be taken as you can’t assume they follow the same rules as the rest of PowerShell.

Error Handling

Everyone’s favorite topic, catching errors and doing something with them. In PowerShell this does not operate as straight forward as C#. With C# if it’s a System.Exception it gets caught using a Try/Catch block, no ifs ands or buts, it just does. This is not the case in PowerShell.

Lets try the following code, I’ll assume the path ‘C:\TimDavis’ does not exist on your machine, if it does we should have a talk about that.

Get-ChildItem -Path "C:\TimDavis"

Running that line of code should result in the following error message being displayed in angry red text.

Get-ChildItem : Cannot find path 'C:\TimDavis' because it does not exist.
At line:1 char:1
+ Get-ChildItem -Path "C:\TimDavis"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\TimDavis:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

Looks like an ItemNotFoundException, we can handle that with our trusty Try/Catch. Modify your test code like so

try {
    Get-ChildItem -Path "C:\TimDavis"
} catch {
    Write-Host "Nothing to see here, all is well!"
}

What happens? Didn’t do what we expected did it?

Get-ChildItem : Cannot find path 'C:\TimDavis' because it does not exist.
At line:2 char:5
+     Get-ChildItem -Path "C:\TimDavis"
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\TimDavis:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

Before you blame a faulty Try/Catch you should realize this was done on purpose.

Not all Errors are equal

In PowerShell there are two kinds of Errors. Terminating and Non-Terminating Errors.

Terminating error is an error that causes execution to halt. Think something so detrimental that the script can’t possibly continue, like syntax issues. These can be caught by default using Try/Catch.

Non-Terminating error is an error that allows PowerShell to continue happily on it’s assigned task. These cannot be caught by default using Try/Catch.

This is a very different mindset to a C# guy, to us an Exception is an Exception is an Exception. If you look at the base class of the ItemNotFoundException listed above guess what it is? System.Exception. I know, it makes no sense, but not halting at every hiccup allows scripts to attempt to continue, which of course as we know is the purpose of good error handling.

Forcing a Terminating Error

Using some of the code from the previous example, lets pretend that this script attempts to get a list of child items of C:\TimDavis and if they are present we do some type of operation to them. If the directory isn’t present then we want to gracefully move on.

$files = Get-ChildItem -Path "C:\TimDavis"

$files | ForEach-Object {
    "Doing something on $($_.Name)"
}

"Script Finished!"

Well we already know that the script generates an error, but it also prints out “Script Finished!” to the console.

Get-ChildItem : Cannot find path 'C:\TimDavis' because it does not exist.
At line:1 char:10
+ $files = Get-ChildItem -Path "C:\TimDavis"
+          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\TimDavis:String) [Get-ChildItem], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
 
Script Finished

This shows the script continuing despite the error behavior we talked about. We have now added our Try/Catch block back.

try {
    $files = Get-ChildItem -Path "C:\TimDavis"

    $files | ForEach-Object {
        "Doing something on $($_.Name)"
    }
} catch {
    Write-Host "Directory doesn't exist, can't do anything"
}

"Script Finished"

Same error as before. Frustrated yet? Hold on, the answer is coming, I promise!

Now we will add the -ErrorAction Stop parameter to the Get-ChildItem call. This instructs PowerShell to treat Non-Terminating errors as Terminating errors. Terminating errors can be caught by Try/Catch blocks. Modify your code to look like this

try {
    $files = Get-ChildItem -Path "C:\TimDavis" -ErrorAction Stop

    $files | ForEach-Object {
        "Doing something on $($_.Name)"
    }
} catch {
    Write-Host "Directory doesn't exist, can't do anything"
}

"Script Finished"

Well this output looks better!

Directory doesn't exist, can't do anything
Script Finished

The Try/Catch handled the now Terminating error. You can use the ErrorAction parameter on just about everything and this can help you catch those angry red issues before they happen.

That is all I have to share with you for now, just remember, different is not always bad and nothing ever works the way you think it should. Play around with some case-insensitive and case-sensitive comparisons with Strings, Arrays and Hashtables to get a feel for what works where.

Start writing some scripts, even simple ones are great. Remember, PowerShell is meant to help us automate our life, handling errors is key to letting a script do it’s job without us babysitting it.


By Tim Davis

I spent my career before Truesec ensuring small financial institutions could meet the demands of todays security standards. I realized that exposure was just the tip of the iceberg and the world of IT security had much more to teach me. I love troubleshooting, finding problems, and being able to bring them to a resolution.

My true passion is programming. I spend my free time writing software for Eve Online and supporting existing deployments. I love learning new programming languages and seeing how I can use it at Truesec.