Powershell: Backtick escaping inconsistent

Created on 11 Oct 2018  路  7Comments  路  Source: PowerShell/PowerShell

Was investigating escaping a filename for PowerShell in https://github.com/PowerShell/PowerShellEditorServices/pull/765#discussion_r224297544.

I created a directory with the following files:

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       10/11/2018  10:57 AM              9 [script.ps1
-a----       10/11/2018  10:57 AM              1 `[script.ps1
-a----       10/11/2018  10:57 AM              1 ``[script.ps1
-a----       10/11/2018  10:57 AM              1 ```[script.ps1
-a----       10/11/2018  10:57 AM              1 ````[script.ps1
-a----       10/11/2018  10:57 AM              8 script.ps1

(New-Item doesn't seem to have a LiteralPath parameter by the way)

I then run the following:

foreach ($i in 0..12) { $ticks = '`' * $i; Get-Item "./$ticks[script.ps1" }

Actual Results

The output of this in the given directory gives me:

Get-Item : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard character pattern is not valid: [script.ps1
At line:1 char:44
+ ... h ($i in 0..12) { $ticks = '`' * $i; Get-Item "./$ticks[script.ps1" }
+                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-Item], ParameterBindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.GetItemCommand



    Directory: C:\Users\roholt\Documents\Dev\sandbox\badnames


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       10/11/2018  10:57 AM              9 [script.ps1
-a----       10/11/2018  10:57 AM              9 [script.ps1
-a----       10/11/2018  10:57 AM              1 `[script.ps1
Get-Item : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard character pattern is not valid: ``[script.ps1
At line:1 char:44
+ ... h ($i in 0..12) { $ticks = '`' * $i; Get-Item "./$ticks[script.ps1" }
+                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-Item], ParameterBindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.GetItemCommand

-a----       10/11/2018  10:57 AM              1 ``[script.ps1
-a----       10/11/2018  10:57 AM              1 `[script.ps1
-a----       10/11/2018  10:57 AM              1 ```[script.ps1
Get-Item : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard character pattern is not valid: ````[script.ps1
At line:1 char:44
+ ... h ($i in 0..12) { $ticks = '`' * $i; Get-Item "./$ticks[script.ps1" }
+                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-Item], ParameterBindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.GetItemCommand

-a----       10/11/2018  10:57 AM              1 ````[script.ps1
-a----       10/11/2018  10:57 AM              1 ``[script.ps1
Get-Item : Cannot find path 'C:\Users\roholt\Documents\Dev\sandbox\badnames\`````[script.ps1' because it does not exist.
At line:1 char:44
+ ... h ($i in 0..12) { $ticks = '`' * $i; Get-Item "./$ticks[script.ps1" }
+                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : ObjectNotFound: (C:\Users\roholt...````[script.ps1:String) [Get-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand

Get-Item : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard character pattern is not valid: ``````[script.ps1
At line:1 char:44
+ ... h ($i in 0..12) { $ticks = '`' * $i; Get-Item "./$ticks[script.ps1" }
+                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-Item], ParameterBindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.GetItemCommand

More concretely:

> Get-Item './[script.ps1'  # 0
# Error (The specified wildcard character pattern is not valid: [script.ps1)
> Get-Item './`[script.ps1'  # 1
# Finds '[script.ps1'
> Get-Item './``[script.ps1'  # 2
# Finds '[script.ps1'                         <--- ???
> Get-Item './```[script.ps1'  # 3
# Finds '`[script.ps1'
> Get-Item './````[script.ps1'  # 4
# Error (The specified wildcard character pattern is not valid: ``[script.ps1)
> Get-Item './`````[script.ps1'  # 5
# Finds '``[script.ps1'
> Get-Item './``````[script.ps1'  # 6
# Finds '`[script.ps1'                         <--- ???
> Get-Item './```````[script.ps1'  # 7
# Finds '```[script.ps1'
> Get-Item './````````[script.ps1'  # 8
# Error (The specified wildcard character pattern is not valid: ````[script.ps1)
> Get-Item './`````````[script.ps1'  # 9
# Finds '````[script.ps1'
> Get-Item './``````````[script.ps1'  # 10
# Finds '``[script.ps1'                         <--- ???
> Get-Item './```````````[script.ps1'  # 11
# Error (Cannot find path 'C:\Users\roholt\Documents\Dev\sandbox\badnames\`````[script.ps1' because it does not exist.)
> Get-Item './````````````[script.ps1'  # 12
# Error (The specified wildcard character pattern is not valid: ``````[script.ps1)

Cases 0, 1, 3, 4, 5, 7, 8, 9, 11 and 12 make sense. Cases 2, 6 and 10 seem pretty strange to me.

Expected Behaviour

Cases 2, 6 and 10 should report an invalid wildcard pattern, like cases 0, 4, 8 and 12.

Tagging @JamesWTruher because we discussed this offline earlier.

WG-Engine-Providers

All 7 comments

Trying to resolve the PSES issue linked above, I also tried the following:

  • Create files called [script.ps1, `[script.ps1, ``[script.ps1, etc.
  • Populate them with the content 0, 1, 2, etc. respectively (so the content of the file represents the number of backticks in the name).
  • Execute them directly in order

Here are my results:

> ./[script.ps1  # 0
# Error (The specified wildcard character pattern is not valid)
> ./`[script.ps1  # 1
# Error (The specified wildcard character pattern is not valid)
> ./``[script.ps1  # 2
1
> ./```[script.ps1 # 3
1
> ./````[script.ps1  # 4
0
> ./`````[script.ps1  # 5
0
> ./``````[script.ps1  # 6
3
> ./```````[script.ps1  # 7
3
> ./````````[script.ps1  # 8
# Error (The specified wildcard character pattern is not valid)
> ./`````````[script.ps1  # 9
# Error (The specified wildcard character pattern is not valid)s
> ./``````````[script.ps1  # 10
# Error (The specified wildcard character pattern is not valid)
> ./```````````[script.ps1  # 11
# Error (The specified wildcard character pattern is not valid)
> ./````````````[script.ps1  # 12
1
> ./`````````````[script.ps1  # 13
1
> ./``````````````[script.ps1  # 14
# Error (The specified wildcard character pattern is not valid)
> ./```````````````[script.ps1  # 15
# Error (The specified wildcard character pattern is not valid)
> ./````````````````[script.ps1  # 16
# Error (The specified wildcard character pattern is not valid)
> ./`````````````````[script.ps1  # 17
# Error (The specified wildcard character pattern is not valid)
> ./``````````````````[script.ps1  # 18
# Error (The specified wildcard character pattern is not valid)
> ./```````````````````[script.ps1  # 19
# Error (The specified wildcard character pattern is not valid)
C:\Users\roholt\Documents\Dev\sandbox\badnames
> ./````````````````````[script.ps1  # 20
2
# I stopped at this point

I also tried invoking with a string literal:

> & '.\[script.ps1'  # 0
# Error (The specified wildcard character pattern is not valid)
> & '.\`[script.ps1'  # 1
1
> & '.\``[script.ps1'  # 2
0
> & '.\```[script.ps1'  # 3
3
> & '.\````[script.ps1'  # 4
# Error (The specified wildcard character pattern is not valid)
> & '.\`````[script.ps1'  # 5
# Error (The specified wildcard character pattern is not valid)
> & '.\``````[script.ps1'  # 6
1
> & '.\```````[script.ps1'  # 7
# Error (The specified wildcard character pattern is not valid)
> & '.\````````[script.ps1'  # 8
# Error (The specified wildcard character pattern is not valid)
> & '.\`````````[script.ps1'  # 9
# Error (The specified wildcard character pattern is not valid)
> & '.\``````````[script.ps1'  # 10
2
> & '.\```````````[script.ps1'  # 11
# Error (The specified wildcard character pattern is not valid)
> & '.\````````````[script.ps1'  # 12
# Error (The specified wildcard character pattern is not valid)
> & '.\`````````````[script.ps1'  # 13
# Error (The specified wildcard character pattern is not valid)
> & '.\``````````````[script.ps1'  # 14
3
# I stopped at this point

After discussing this further with @JamesWTruher, we think one way to get around this is to use the fully provider-qualified name: Microsoft.PowerShell.Core\FileSystem::C:\path\to\`[script.ps1

@rjmholt Please look #7399 - perhaps it could resolve the issue (fix for ContainsValidWildcardPattern).

I believe this is more related to the WildcardPatten escape & unescape inconsistency mentioned in #7407

Re invocation with . / &: methinks the command-name argument should never be interpreted as a wildcard expression (though fixing that would technically be a breaking change) - see #4726

To add to the cases of unexpected behaviors with -Path: an argument that also contains _unescaped_ wildcard characters introduces the unexpected need for an additional layer of escaping:

$null = New-Item 'a['

Get-Item 'a`['  # OK - no unescaped metachar.

Get-Item '*`['  # BREAKS - unescaped metachar. *

Get-Item '*``['  # WORKS, but shouldn't be necessary

Also, interestingly, performing the filtering via -Include does _not_ exhibit the bug:

Get-ChildItem * -Include '*`['  # OK
Get-Item '*``['  # WORKS, but shouldn't be necessary

I think this is the most important problem here; the input is unescaped a varying (and hard-to-predict) number of times, which undermines the whole concept of escaping to my mind.

I think I found one more doubtful behavior. Please check if it's right

Reproduce

working directory:

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        2019-06-14 鞓ろ泟 10:05                []
d-----        2019-06-14 鞓ろ泟 10:50                ``

Then run the following:

gi '``[``]'

Expected

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        2019-06-14 鞓ろ泟 10:50                ``

'``[``]' should be interpreted as BACKTICK META-LBRACKET BACKTICK META-RBRACKET, then match ``

Actual

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----        2019-06-14 鞓ろ泟 10:05                []
Was this page helpful?
0 / 5 - 0 ratings