Question:
When you include something like [1]
in a file name like File[1].txt
used with the Invoke-WebRequest
and the -OutFile
parameter you get an error Cannot perform operation because the wildcard path File[1].txt did not resolve to a file.
This is caused by the behavior documented here.
With other cmdlets you would use -LiteralPath
to force the path to be taken literally but in this case that is not an option.
I have tried escaping the [
and ]
characters with or
\
but it still gives the same error.
To simplify testing you can reproduce the same issue with Out-File
, Test-Path
, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#Fails Out-File -FilePath "file[1].txt" Out-File -FilePath "file`[1`].txt" Out-File -FilePath "file\[1\].txt" #Succeeds Out-File -LiteralPath "file[1].txt" #Fails Test-Path -Path "file[1].txt" #Succeeds Test-Path -LiteralPath "file[1].txt" |
How can I escape characters that would be used to express wildcards in -Path
, -FilePath
, -OutFile
, etc. so that they function like the string was specified with -LiteralPath
since -LiteralPath
isn't available with Invoke-WebRequest
?
Answer:
Update:
- In PowerShell (Core) 7.1+, file paths passed to the
-OutFile
parameter ofInvoke-WebRequest
andInvoke-RestMethod
are now interpreted literally:- That is,
-OutFile
now acts like-LiteralPath
,[1] and there is no longer a need to escape[
and]
characters, so that the following example command works as-is:
123# PowerShell 7.1+ only.Invoke-WebRequest http://example.org -OutFile File[1].txt
- That is,
- Therefore, the following applies only to Windows PowerShell (and to now-obsolete PowerShell (Core) versions v7.0 and below):
Escaping the [
and ]
characters as [ and
]
so that they are treated literally when interpreted as a wildcard expression with -Path
(-FilePath
) and -OutFile
unfortunately only half works at the moment, due to a bug discussed in the bottom section:
- Performing the escaping ensures that the target parameter accepts the path (the command doesn't break anymore) ...
- ... but on creation of the file it is mistakenly the escaped representation that is used as the literal filename - see bottom section.
Workaround for now:Tip of the hat to hashbrown for helping to simplify it.
- Make
Invoke-RestMethod
/Invoke-WebRequest
save to a temporary file... - ... and then rename (move) the temporary file to the desired output file path.
1 2 3 4 5 6 7 8 9 10 11 |
# Literal output file path. $outFile = '.\file[1].txt' # Simulate a call to Invoke-RestMethod / Invoke-WebRequest -OutFile. # Save to a *temporary file*, created on demand - such # a temporary file path can be assumed to never contain '[' or ']' 'hi' | Out-File -FilePath ($tempFile = New-TemporaryFile) # Rename (move) the temporary file to the desired target path. Move-Item -Force -LiteralPath $tempFile -Destination $outFile |
In Windows PowerShell v4-, use [IO.Path]::GetTempfileName()
in lieu of New-TemporaryFile
.
Escaping [literal] paths for use as wildcard patterns:
Use any of the following string-literal representations, which ultimately result in the same string with verbatim content file
[1].txt
, which, when interpreted as a wildcard expression, is the escaped equivalent of literal string file[1].txt
:
'file
[1].txt'
"file
[1
].txt"
file
[1
].txt
To create this escaping programmatically, use:
1 2 3 |
$literalName = 'file[1].txt' $escapedName = [WildcardPattern]::Escape($literalName) # -> 'file`[1`].txt' |
What matters is that the target cmdlet sees the [
and ]
as -escaped in the
-Path
(-FilePath
) argument it is passed for them to be treated verbatim.
If you use "..."
quoting or an unquoted argument (which mostly behaves as if it were enclosed in "..."
), PowerShell’s string parsing gets in the way:
through, you must escape it itself, as is also used as the escape character inside expandable strings (
"..."
), so in order to pass
.
- Otherwise something like
is “eaten” – because[
inside"..."
turns into just[
- the
.[
is an escaped[
from"..."
's perspective, and escaping a character that doesn't need escaping turns into just that character; in short: both"file
[1].txt"
andfile
[1].txt
turn into plainfile[1].txt
, as if you had never used
By contrast, characters are used verbatim inside
'...'
-quoted strings and need no escaping.
Flawed file-creation behavior of many cmdlets with -Path
:
The bug mentioned above - that on file creation the escaped representation is mistakenly used as the literal filename - affects most cmdlets, unfortunately: That is, they unexpectedly retain the characters in the escaped pattern on creating a file, so that by specifying
-Path 'file
[1].txt'
you’ll end up with a file literally named file
.[1
].txt
Fortunately, most cmdlets do support -LiteralPath
, so use of -LiteralPath file[1].txt
is the better choice anyway and avoids this bug.
Some of the affected cmdlets:
Invoke-WebRequest
andInvoke-RestMethod
Out-File
and therefore also redirection operators>
and>>
, which effectively callOut-File
behind the scenes.- Note that
Set-Content
andAdd-Content
do not exhibit this problem. - All(?)
Export-*
cmdlets. - Others?
The bug has been reported in GitHub issue #9475.
[1] This was technically a breaking change, but it was considered acceptable, due to the counterintuitive nature of the original behavior. Unfortunately, the counterintuitive behavior still surfaces in many other contexts – including still with Out-File
unless -LiteralPath
is explicitly used. See GitHub issue #17106 for a summary.