tl;dr
To ensure that file-path argument $var
in redirection operations
> $var
/ >> $var
is treated literally if it contains [
and ]
, use
| Out-File -LiteralPath $var
/ | Out-File -LiteralPath $var -Append
instead.
Copy-Item -LiteralPath $inputPath -Destination $outputPath 2>&1 |
ForEach-Object { "$_" } |
Out-File -LiteralPath $logfile # literal alternative to >> $logfile
As in your case, you may have to combine it with a redirection in order to ensure that additional streams beside the success output stream are targeted:
# Literal equivalent of *>> $logfile
... *>&1 | Out-File -LiteralPath $logfile -Append
Unfortunately, it gets tricky with capturing a given other stream only, as you then need to use the appropriate -*Variable
common parameter, such as the common -ErrorVariable
parameter:
# Literal equivalent of 2>> $logfile
... -ErrorVariable errs
$errs | Out-File -LiteralPath $logfile -Append
Caveat:
It is tempting to try to bypass the above solutions by escaping the literal path so that when it is interpreted as a wildcard, it is treated literally, using [WildcardPattern]::Escape()
.
However, this does not work as expected as of PowerShell 7.2.6, because the escaped form of the literal path is then used as the literal file path - see GitHub issue #9475 and also the discussion about whether >
/ >>
, ... should treat their arguments as wildcards to begin with.
# !! Arguably SHOULD work, but doesn't as of 7.2.6:
# The file that is created is LITERALLY named for the ESCAPED form:
# `[1`].txt
'hi' > ([WildcardPattern]::Escape('[1].txt'))
Background information:
The -Path
(-FilePath
) parameter of PowerShell's provider cmdlets expects wildcard expressions for targeting file-system items by a name or path pattern. This parameter is implicitly targeted by the first positional argument, i.e. if you don't explicitly name the target parameter - e.g., Get-ChildItem foo
is implicitly the same as Get-ChildItem -Path foo
.
Surprisingly, this doesn't just apply to file-reading cmdlets (e.g., Get-ChildItem
, Get-Content
), but also to file-writing cmdlets (e.g., Set-Content
, Out-File
).
- Note that the parameter named
-Path
is called -FilePath
in Out-File
for historical reasons. In PowerShell (Core) 7+, -Path
was introduced as an alias name, for consistency.
Arguably, in file-writing/creating cmdlets this behavior is inappropriate - given that you usually know exactly what literal path you want to target - and there are several GitHub issues discussing this; an overview of all relevant discussions can be found in this comment on GitHub issue #17106.
In order for such arguments to be treated literally (verbatim), you must
use the -LiteralPath
parameter instead.
In effect, > $var
and >> $var
are aliases for | Out-File -FilePath $var
and | Out-File -FilePath $var -Append
, which implies that $var
is interpreted as a wildcard.
Using explicit Out-File
calls with -LiteralPath
, as shown at the top, avoids this problem.