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.
Invoke-WebRequest https://raw.githubusercontent.com/PowerShell/PowerShell/master/README.md -OutFile ReadMe[Powershell].md
Creates a ReadMe[Powershell].md file in current directory.
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
> $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
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:
`
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:
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:
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.