1

I've got a Powershell script that takes a filename as input.

param([string]$folder, [string]$input_filename)
$logfile=$folder + "\duplicate.log"
Write-Output "`nScript: `n$transformation_filename`nLogfile: " | Out-file $logfile -Append

This works fine for most filenames, but it breaks down when the filename or folder name contains [].

dupfile.ps1 "C:\foldername [w]" "filename.xml"

Line 3 throws this error: Cannot perform operation because the wildcard path C:foldername [w]\duplicate.log did not resolve to a file.

So the variables have to be treated more literally.

$logfile='$folder' + "\duplicate.log"

Nope, that's too literal.

$logfile="$folder" + "\duplicate.log"

Nope, same error.

How can I tell Powershell to insert the variable value but don't try to interpret anything in that value as wildcards?

Edit: I'm using Powershell 5. My dupfile.ps1 contains only the 3 lines above. Using this from the PS shell to run the script: C:_SCRIPTS_\dupfile.ps1 "C:\foldername [w]" "test.txt"

One more addition, if I may. I have a few lines that redirect their error messages to the logfile.

Copy-Item -LiteralPath  $inputPath -Destination $outputPath 2>&1|%{ "$_" } >>$logfile

The redirection trips over the [] again. I don't think I can use -LiteralPath here.

Hobbes
  • 1,964
  • 3
  • 18
  • 35
  • 3
    Which *PowerShell* command actually causes the error? You can probably use the `-LiteralPath` parameter there (as apposed to the default `-Path` parameter). – iRon Aug 12 '22 at 09:34
  • None of the above lines will throw that error, except maybe `dupfile.ps1 ...`. But unfortunateley we cannot look into that file. Please provide a [MRE]. – stackprotector Aug 12 '22 at 09:41
  • I've added the requested info. – Hobbes Aug 12 '22 at 09:49
  • As @iRon already suggested: `Out-File -LiteralPath $logfile ...` – stackprotector Aug 12 '22 at 09:55
  • the -LiteralPath works for most of what I need, except when I redirect error messages. Any ideas on that? – Hobbes Aug 12 '22 at 10:10
  • Does this answer your question? [Function to escape characters in paths](https://stackoverflow.com/q/46037095/11942268) – stackprotector Aug 12 '22 at 11:30
  • @stackprotector, trying `[WildcardPattern]::Escape()` is tempting and arguably _should_ work, but doesn't: the _escaped_ form of the path is used _literally_ when the file is created. – mklement0 Aug 12 '22 at 14:03

1 Answers1

2

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775