Weekend Scripter: Unexpected Case Sensitivity in PowerShell

Summary: PowerShell MVP, Mike F Robbins, discusses case sensitivity in Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Welcome back guest blogger, Mike Robbins.

Photo of Mike Robbins

Mike F Robbins is a Microsoft MVP for Windows PowerShell and a SAPIEN Technologies MVP. He is a co-author of Windows PowerShell TFM 4th Edition, and is a contributing author of a chapter in the PowerShell Deep Dives book. Mike has written guest blog posts for the Hey, Scripting Guy! Blog, PowerShell Magazine, and PowerShell.org. He is the winner of the advanced category in the 2013 PowerShell Scripting Games. Mike is also the leader and co-founder of the Mississippi PowerShell User Group. He blogs at mikefrobbins.com and can be found on twitter @mikefrobbins.

A few months ago I wrote a blog post called Some Cases of Unexpected Case Sensitivity in PowerShell, and I thought I would expand on that topic a bit here as a sequel to that original post.

Everything is case insensitive in PowerShell, right? Well, that’s what we’re normally taught, but it’s actually not quite that simple. The answer to whether PowerShell is case sensitive is, “It depends.” In general, PowerShell is not case sensitive, but there are a number of caveats to case sensitivity, some of which are intentional, and some that are unexpected.

Operators

By default, the comparison operators that you’ll commonly see used with PowerShell are case insensitive. These include:

  • -eq
  • -ne
  • -gt
  • -ge
  • -lt
  • -le
  • -like
  • -notlike
  • -match
  • -notmatch
  • -contains
  • -notcontains
  • -in
  • -notin
  • -replace

Each of these comparison operators have a corresponding case-sensitive version:

  • -ceq
  • -cne
  • -cgt
  • -cge
  • -clt
  • -cle
  • -clike
  • -cnotlike
  • -cmatch
  • -cnotmatch
  • -ccontains
  • -cnotcontains
  • -cin
  • -cnotin
  • -creplace

Each of these operators also has a case-insensitive version that begins with an i instead of a c. These versions are explicitly case insensitive, although you’ll rarely (if ever) see them used because their behavior is the same as where i or c is not specified.

Here is an example of using these operators:

PS C:\> 'PowerShell' -eq 'powershell'

True

PS C:\> 'PowerShell' -ceq 'powershell'

False

PS C:\> 'PowerShell' -ieq 'powershell'

True

PS C:\>

To learn more about comparison operators, see the about_Comparison_Operators Help topic in PowerShell or view the online version: about_Comparison_Operators.

Escape characters

All of the alphabet-based escape characters are case sensitive:

  • `a    Alert
  • `b    Backspace
  • `f    Form feed
  • `n    New line
  • `r    Carriage return
  • `t    Horizontal tab
  • `v    Vertical tab

The special meaning of these characters doesn’t occur when an upper-case character is specified, for example:

PS C:\> "Hey, Scripting `n Guy! Blog"

Hey, Scripting

 Guy! Blog

PS C:\> "Hey, Scripting `N Guy! Blog"

Hey, Scripting N Guy! Blog

PS C:\>

To learn more about escape characters, see the about_Escape_Characters Help topic in PowerShell or view the online version: about_Escape_Characters.

Region and EndRegion tags

Regions were introduced in Windows PowerShell 3.0. Specifying the #region or #endregion tags in anything other than lower-case letters breaks the ability to collapse that portion of the code.

External technologies

When you’re using PowerShell to access information from a technology that’s outside of PowerShell, such as from Active Directory with ADSI, the rules for case sensitivity in PowerShell no longer apply. An example is shown in the following function. If the case for samaccountname is changed inside of pscustomobject, the samaccountname will not be returned.

#Requires -Version 3.0

function Get-MrADUser {   

    [CmdletBinding()]

    param(

        [Parameter(Mandatory,

                   ValueFromPipeline,

                   ValueFromPipelineByPropertyName)]

        [String[]]$UserName

    )   

    PROCESS {       

        foreach ($user in $UserName){           

            $Search = [adsisearcher]"(&(objectCategory=person)(objectClass=user)(samaccountname=$user))"

            foreach ($user in $($Search.FindAll())){               

                $stringSID = (New-Object -TypeName System.Security.Principal.SecurityIdentifier($($user.Properties.objectsid),0)).Value

                $objectGUID = [System.Guid]$($user.Properties.objectguid)

                [pscustomobject]@{

                    DistinguishedName = $($user.Properties.distinguishedname)

                    Enabled = (-not($($user.GetDirectoryEntry().InvokeGet('AccountDisabled'))))

                    GivenName = $($user.Properties.givenname)

                    Name = $($user.Properties.name)

                    ObjectClass = $($user.Properties.objectclass)[-1]

                    ObjectGUID = $objectGUID

                    SamAccountName = $($user.Properties.samaccountname)

                    SID = $stringSID

                    Surname = $($user.Properties.sn)

                    UserPrincipalName = $($user.Properties.userprincipalname)

                }

            }

        }

    }

}

Cmdlets

Some cmdlets have parameters to control case sensitivity. One example is the Sort-Object cmdlet, which has a CaseSensitive parameter. Specifying this parameter when using Sort-Object indicates that the sort should be case sensitive. By default, sorting with Sort-Object is case insensitive. Here are some examples:

PS C:\> 'PowerShell', 'powershell', 'POWERSHELL' | Sort-Object

POWERSHELL

powershell

PowerShell

PS C:\> 'PowerShell', 'powershell', 'POWERSHELL' | Sort-Object -CaseSensitive

powershell

PowerShell

POWERSHELL

PS C:\>

Switch statements

Case-insensitive switch statements are not supported in PowerShell workflows. For more detailed information about this topic and to learn how to work around this, see the following Hey, Scripting Guy! Blog post: PowerShell Workflows: Restrictions.

Hash tables

Hash tables are case insensitive by default. Notice that I can’t add ComputerName and computername to the hash table in the following example because, by default, the keys are the same:

PS C:\> $hashtable = @{}

PS C:\> $hashtable.Add('ComputerName', 'PC01')

PS C:\> $hashtable.Add('computername', 'pc01')

Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary: 'ComputerName'  Key

being added: 'computername'"

At line:1 char:1

+ $hashtable.Add('computername', 'pc01')

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

    + FullyQualifiedErrorId : ArgumentException

PS C:\>

A special case-sensitive hash table can be created though. Notice in the following example, I can add the two keys that failed in the previous example:

PS C:\> $hashtable = New-Object -TypeName System.Collections.Hashtable

PS C:\> $hashtable.Add('ComputerName', 'PC01')

PS C:\> $hashtable.Add('computername', 'pc01')

PS C:\> $hashtable

Name                           Value

—-                                 —–

ComputerName                 PC01

computername                   pc01

PS C:\>

JSON

JSON is case sensitive, but the custom objects that are created when using the ConvertFrom-Json cmdlet are case insensitive. For more detailed information abouit JSON, see this Hey, Scripting Guy! Blog post: JSON Is the New XML.

Methods

As you can see in the following example, methods such as contains and replace are case sensitive, whereas the contains and replace operators are not case sensitive.

PS C:\> $Features = 'SQLENGINE', 'SSMS', 'ADV_SSMS'

PS C:\> $Features.Contains('SQLENGINE')

True

PS C:\> $Features -contains 'SQLENGINE'

True

PS C:\> $Features.Contains('SQLEngine')

False

PS C:\> $Features -contains 'SQLEngine'

True

PS C:\>

Do you think that you’ll outsmart the case-sensitivity issues in PowerShell by using the ToUpper or ToLower method to convert strings to a specific case before performing comparison operations on them? Not so fast. You could be creating a different problem—especially if you’re potentially working with other cultures. If you’re thinking about going this route, consider reading my blog post Using Pester to Test PowerShell Code in Other Cultures.

~Mike

Thanks for clarifying a somewhat confusing issue for us, Mike.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy