Powershell: Discussion: Use literal paths for OutFile parameter on Invoke-RestMethod / Invoke-WebRequest

Created on 20 Feb 2017  路  8Comments  路  Source: PowerShell/PowerShell

When using square brackets in the OutFile parameter of the web cmdlets the square brackets in the path act as a wildcard option/range operator.
Hence any square brackets in the OutFile parameter causes an error for the cmdlets.
Personally, when downloading a file, I always use a literal path, instead of specifying a file with wildcard support.

I would like to start a discussion if it makes sense to treat the OutFile parameter as a literal path.
Any opinions on that?

Maybe someone could contribute scenarios where wildcard support makes sense.

Note: Of course there are several workarounds in PowerShell to overcome this minor lack of comfort.

Steps to reproduce

Invoke-WebRequest https://raw.githubusercontent.com/PowerShell/PowerShell/master/README.md -OutFile ReadMe[Powershell].md

Expected behavior

Creates a ReadMe[Powershell].md file in current directory.

Actual behavior

Invoke-WebRequest : Cannot perform operation because the wildcard path ReadMe[Powershell].md did not resolve to a file.
At line:1 char:1
+ Invoke-WebRequest https://raw.githubusercontent.com/PowerShell/PowerS ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OpenError: (ReadMe[Powershell].md:String) [Invoke-WebRequest], FileNotFoundException
    + FullyQualifiedErrorId : FileOpenFailure,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

Environment data

> $PSVersionTable
Name                           Value
----                           -----
PSVersion                      5.1.14393.693
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.14393.693
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
Area-Cmdlets-Utility Committee-Reviewed

Most helpful comment

As I recall, Chris, when escaping a path like this you have to double up, or use a literal string. For example:

Invoke-RestMethod $Uri -OutFile C:\``[test``].txt
# or
Invoke-RestMethod $Uri -OutFile 'C:\`[test`].txt'

This is because typically the bare strings in a parameter are treated as expandable strings, which means that the backtick is treated as a PS escape character first and evaluated before being passed to the internals. As a result, you need to escape the escape character so PS's expandable string evaluation doesn't eat it. The other option is to force PS to recognise it as a literal string.

All 8 comments

Agree that -OutFile makes more sense as a literal path. (It only makes sense as a literal path?). If the wildcard resolves to multiple files then Invoke-WebRequest throws an exception.

But there could be scripts which depend on the wildcard resolving to a single filename - that does work, so it might be a breaking change. In that case -LiteralOutFile as a new parameter?

Here is the code which saves to the file referencing a property QualifiedOutFile, which is defined here, it calls return (QualifyFilePath(OutFile));

That's defined here

    private string QualifyFilePath(string path)
    {
        string resolvedFilePath = PathUtils.ResolveFilePath(path, this, false);
        return resolvedFilePath;
    }

The third parameter sets whether ResolveFilePath treats it as literal or not, so changing that to true should make it resolve as a literal path and fix this.

I can't see any other calls to QualifyFilePath, suggests there wouldn't be much other impact, but I'm not certain.

I think in general, output paths should be literalpaths. The behavior of matching a file seems unpredictable at best and clobbering something unintentional at worst.

@PowerShell/powershell-committee reviewed this and believe the right approach is to rename -OutFile to -Path (aliased as -OutFile) and add a -LiteralPath parameter. Existing -OutFile can be used with current globbing behavior to match a single file using a wildcard. For example, if you have a file called helloworld.txt and you want to overwrite it with the output, you can use -OutFile hello*.

@ThreeFive-O You mention there are work arounds for this, can you add them as a comment?

I am trying to OutFile to a file that has [1] in the name but I cannot figure out how to escape the [] characters. I tried ` and \ but they do not escape the [] characters.

As I recall, Chris, when escaping a path like this you have to double up, or use a literal string. For example:

Invoke-RestMethod $Uri -OutFile C:\``[test``].txt
# or
Invoke-RestMethod $Uri -OutFile 'C:\`[test`].txt'

This is because typically the bare strings in a parameter are treated as expandable strings, which means that the backtick is treated as a PS escape character first and evaluated before being passed to the internals. As a result, you need to escape the escape character so PS's expandable string evaluation doesn't eat it. The other option is to force PS to recognise it as a literal string.

@vexx32:

While your escaping technique is correct and ensures that the file path is _accepted_, there is a _bug_ that affects most cmdlets in that they then use the _escaped_ path as a _literal_ path on output-file creation, which means that the ` chars. are unexpectedly _retained_ - see #9475.

Therefore, the cumbersome workaround for now is to:

  • escape the input path so that it is _accepted_
  • and afterwards _rename_ the incorrectly named file by removing ` instances from it.

See this Stack Overflow answer for details.

Well now, that's no good at all! Thanks for the links!

:tada:This issue was addressed in #11701, which has now been successfully released as v7.1.0-preview.4.:tada:

Handy links:

Was this page helpful?
0 / 5 - 0 ratings