2

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.

7
  • 3
    Which PowerShell command actually causes the error? You can probably use the -LiteralPath parameter there (as apposed to the default -Path parameter). Commented Aug 12, 2022 at 9: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 minimal reproducible example. Commented Aug 12, 2022 at 9:41
  • I've added the requested info. Commented Aug 12, 2022 at 9:49
  • As @iRon already suggested: Out-File -LiteralPath $logfile ... Commented Aug 12, 2022 at 9:55
  • the -LiteralPath works for most of what I need, except when I redirect error messages. Any ideas on that? Commented Aug 12, 2022 at 10:10

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.