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?
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?
3 Answers
Reset to default 3To 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
To match files that start with test[xml]
using a single glob in PowerShell, you need to escape the brackets properly.
Resolve-Path -Path 'test``[xml``].*'
In PowerShell, the backtick (`) is used as the escape character. Since you need to escape the brackets, you should use double backticks to escape each bracket due to a bug. Hopefully, they will fix it in upcoming version.
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.
- As you point out, because
Use filtering via
-like
, the wildcard matching operator, in a post-processing step viaWhere-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.
- The caveat is that the wildcard "language" of the
'test``[xml``]*'
– Mathias R. Jessen Commented Mar 6 at 11:10-LiteralPath
or a single backtick to escape the meta characters.-LiteralPath
doesn't answer the question, and using a single literal backtick doesn't work here; why the latter doesn't work is a total mystery... – Fravadona Commented Mar 6 at 12:17