Search code examples
stringpowershellvariablescontrol-characters

Unexpected behavior with a string stored in a variable in PowerShell


I'm getting some odd behavior from Excel's Cells.Find() method:

Variable I'm searching on:

PS > $volumename
vol_01       

PS > $volumename.GetType()

IsPublic IsSerial Name                                     BaseType                                                                                                                                            
-------- -------- ----                                     --------                                                                                                                                            
True     True     String                                   System.Object 

produces no results:

PS > $sheet.Cells.Find($volumename).Row

but if I manually copy and paste the value of that variable:

PS > $volumename = "vol_01"
PS > $volumename.GetType()

IsPublic IsSerial Name                                     BaseType                                                                                                                                            
-------- -------- ----                                     --------                                                                                                                                            
True     True     String                                   System.Object 

Gets the value I am expecting:

PS > $sheet.Cells.Find($volumename).Row
198

They appear to be exactly the same type in every way to me. This doesn't happen for every case. Some volume names passthrough fine while others do not. I did scrub the volume name for this post as it has a customers naming convention. It is the same format as above and the same format as the volume names that work.


Solution

  • The following snippet can be used to inspect a string for hidden control characters:

    PS> & { [int[]] [char[]] $Args[0] | 
          % { '0x{0:x} [{1}]' -f $_, [char] $_ } } "vol_01`n"
    
    0x76 [v]
    0x6f [o]
    0x6c [l]
    0x5f [_]
    0x30 [0]
    0x31 [1]
    0xa [
    ]
    

    The first column is each character's Unicode code point ("ASCII code"), and the second column the character itself, enclosed in [...]

    Note that I've added "`n" at the end of the string - a newline character (U+000A) - whose code point expressed as a hex. number is 0xa.

    If, as in your case, the only unwanted part of the string is trailing whitespace, you can remove it as follows:

    $volumename.TrimEnd() # trim trailing whitespace
    

    In your case, the trailing whitespace is 0xa0, the NO-BREAK SPACE (U+00A0), which .TrimEnd() also removes, as Tom Blodget points out.


    Simple function wrapper based on the above, for use with pipeline input:

    filter debug-Chars { [int[]] [char[]] $_ | % { '0x{0:x} [{1}]' -f $_, [char] $_ } }
    

    Sample use:

    "vol_01`n" | debug-Chars
    

    A more sophisticated function, named Debug-String, is available as an MIT-licensed Gist: It uses PowerShell's own escape-sequence notation to represent control characters in the input string, and also visualizes spaces as · by default; adding -UnicodeEscapes uses escape sequences for all non-ASCII-range characters.

    Here's an example that first downloads and defines the function:

    # Download and define the Debug-String function.
    # NOTE: 
    #  I can personally assure you that doing this is safe, but you
    #  you should always check the source code first.
    irm https://gist.github.com/mklement0/7f2f1e13ac9c2afaf0a0906d08b392d1/raw/Debug-String.ps1 | iex
    
    "A b`nThis is a hyphen: '-', this is an en dash '–'" | Debug-String -SingleLine
    

    Output:

    screenshot