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!
Insensitive | Sensitive | Explicit 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 |
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.