Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Globbing/Wildcard problem with PowerShell `-Path` cmdlets

Tags:

powershell

I have some files with "meta-characters" in their name, for eg. brackets:

'test[txt].0',  'test[xml].0', 'test[xml].1' | % {New-Item $_} | Out-Null

I can get those files with a glob:

Resolve-Path -Path 'test*'

Path
----
C:\test[txt].0
C:\test[xml].0
C:\test[xml].1

Or a single one of them using backtick-escapes:

Resolve-Path -Path 'test`[xml`].0'

Path
----
C:\test[xml].0

But I can't get the ones that start with test[xml]. using backtick-escapes and a "wildcard":

Resolve-Path -Path 'test`[xml`].*'

What would be the correct -Path to use?

like image 294
Fravadona Avatar asked Sep 20 '26 12:09

Fravadona


2 Answers

Preface:

  • In cases where paths are meant to be literal ones that just so happen to contain [ and ] characters, the proper solution is to use the -LiteralPath parameter instead of the (possibly positionally implied) -Path parameter, as discussed in this answer;
    -LiteralPath can be shorted to -lp in PowerShell (Core) 7.

  • In the case at hand, the paths are by design PowerShell wildcard patterns.


You're seeing a long-standing bug that affects all provider cmdlets (including Get-ChildItem, for instance), first reported in 2018 in GitHub issue #7999, under the title "Backtick escaping inconsistent", which captures the gist of the problem:

  • Situationally, for no apparent reason, you must doubly escape [ and ] characters in order for them to be treated literally rather than as wildcard metacharacters, i.e. as verbatim ``[ and ``]

  • Also, sometimes you get away with escaping only [ (not also ]), such as in the case at hand (which is how it should be, given that an ] in isolation is not a metacharacter):

    # Effective for now, but MAY BREAK IN THE FUTURE.
    Resolve-Path './test``[xml].*'
    
  • The specific variation of the bug is that the double escaping unexpectedly becomes necessary due to escaped [ (and possibly ]) being combined with (unescaped) * or ?.

On a general note:

  • Since `, the so-called backtick also serves as the escape character in expandable (interpolating) strings, "..." (as opposed to verbatim strings, '...'), each ` to be passed through to the target command must be doubled again; this also applies to bareword arguments (unquoted command arguments), which are implicitly treated like expandable strings.

  • Therefore, the equivalents of the command above are:

    # Double-quoted argument.
    Resolve-Path "./test````[xml].*"
    
    # Bareword argument
    Resolve-Path ./test````[xml].*
    

Caveat:

  • While the above workaround and its syntactic variations are effective for now, they will break should the bug be fixed in a future PowerShell 7 version.

  • See the next section for future-proof workarounds.


Alternative, future-proof workarounds, where escaping works as expected:

  • As shown in your own answer, the simplest workaround is to make [ part of a character set ([...]), inside of which escaping is not needed; that is, use [[]:

      Resolve-Path ./test[[]xml].*
    
    • As you point out, because `-escaping is then not involved, this has the added advantage of not needing to vary the pattern based on whether '...' vs. "..." quoting / a bareword is used.
  • Use filtering via -like, the wildcard matching operator, in a post-processing step via Where-Object; while less efficient, this works predictably:

      Resolve-Path * | Where-Object Path -like '*[\/]test`[xml].*'
    
      # With Get-ChildItem, more simply:
      Get-ChildItem | Where-Object Name -like 'test`[xml].*'
    
  • In the context of Get-ChildItem, if the wildcard metacharacters are confined to the last path component, you can express that component via a -Filter argument, in which [ and ] do not require escaping:

      # Note that -LiteralPath . can be omitted here.
      Get-ChildItem -LiteralPath . -Filter test[xml].*
    
    • The caveat is that the wildcard "language" of the -Filter parameter generally differs from PowerShell's own wildcards: in addition to -Filter not supporting character sets and ranges (e.g., [abc] or [a-c]) - which is why [ and ] don't require escaping - the matching has legacy quirks for backward compatibility - see this answer for details.
like image 173
mklement0 Avatar answered Sep 22 '26 09:09

mklement0


To work around this bug, a possible solution is to avoid using any backtick-escape sequence; just enclose the opening bracket inside brackets:

Resolve-Path -Path test[[]xml].*

Path
----
C:\test[xml].0
C:\test[xml].1

remark: here it doesn't matter if test[[]xml].* is unquoted, single-quoted or double-quoted

like image 38
Fravadona Avatar answered Sep 22 '26 10:09

Fravadona



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!