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?
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].*
`-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].*
-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.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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With