Note: This seems to affect all file-creating cmdlets - including > and >> - with the laudable exception of Set-Content and Add-Content.
Conceptually related: #7999
Say you want to create a file literally named file[1].txt.
In order to successfully pass this name to a -Path parameter (or -FilePath or -OutFile), it must be escaped as file`[1`].txt, because -Path parameters interpret their arguments as wildcard expressions.
While such an escaped path is _accepted_, the problem is that _it isn't retranslated into its literal equivalent on file creation_, resulting in the _escaped_ representation being used as the filename; in the example above, you end up with a file literally named file`[1`].txt instead of the intended file[1].txt
This problem can generally be avoided by passing the filename to the -LiteralPath parameter instead, but that is not yet always an option - see #3174 - and, of course, the bug is worth fixing either way.
Run the following Pester tests:
Describe '-Path file-creation test' {
BeforeAll { Push-Location TestDrive:\ }
AfterAll { Pop-Location }
It "Escaped wildcard paths are created as their literal equivalents" {
'hi' | Out-File -FilePath 'file`[1`].txt'
Test-Path -LiteralPath 'file[1].txt' | Should -Be $true
Test-Path -LiteralPath 'file`[1`].txt' | Should -Be $false
}
}
The tests should pass.
The tests fail, because the actual name of the file created is literally file`[1`].txt, i.e., the _escaped_ name.
PowerShell Core 6.2.0 / Windows PowerShell v5.1
https://github.com/PowerShell/PowerShell/pull/9258 is related to this but I am not sure if it covers all situations.
It feels to me the right behavior for Out-File -FilePath may be:
Out-File -FilePath [ab].txt should be able to target the existing file a.txt, and Out-File -FilePath '[out].txt' should be able to target the existing file [out].txt.-FilePath should be used as literal path to create the new file, just like what New-Item does, meaning Out-File -FilePath [hello].txt should create '[hello].txt' when no existing file found that matches the wildcard pattern.> and >> may be better always use -LiteralPath parameter when invoking Out-File. But this will be a breaking change.The part I don't like about this proposal is that Out-File -FilePath would target different files depending on if an existing file that matches the wildcard pattern exists or not. For example, Out-File -FilePath [ab].txt would target a.txt if that file already exists, and [ab].txt if no matching file found.
This is a general issue that exists for all cmdlets that do operations that search existing files before create new files.
https://github.com/PowerShell/PowerShell/pull/9258 is an attempt to address the issue reported here for Out-File -FilePath. But I think this is a general issue that should be fixed in a wider scope.
I have always been surprised that the behavior of the path (literalpath) parameters varies in different cmdlets. This causes an endless stream of fixes.
It would be great if the paths (literalpaths) always behaved in the same way, and any deviations were regulated by separate parameters.
It feels to me the right behavior for
Out-File -FilePathmay be:
- when searching for an existing file, wildcard and escaped wildcard characters are respected, meaning
Out-File -FilePath [ab].txtshould be able to target the existing filea.txt, andOut-File -FilePath '[out].txt'should be able to target the existing file[out].txt.- when no existing file is found, the provided path to
-FilePathshould be used as literal path to create the new file, just like whatNew-Itemdoes, meaningOut-File -FilePath [hello].txtshould create '[hello].txt' when no existing file found that matches the wildcard pattern.- redirection operator
>and>>may be better always use-LiteralPathparameter when invokingOut-File. But this will be a breaking change.The part I don't like about this proposal is that
Out-File -FilePathwould target different files depending on if an existing file that matches the wildcard pattern exists or not. For example,Out-File -FilePath [ab].txtwould targeta.txtif that file already exists, and[ab].txtif no matching file found.
I believe it is not a good idea to have a single parameter handling two different usages that might conflict with each other. For example, how do we tell if a user typed an incorrect wildcard accidentally, or a literal filename intentionally? To keep the things simple, I would prefer to use the existing Path/LiteralPath concept:
-FilePath an alias of -Path-LiteralPath to the cmdletAs for > and >>, would it be possible to add configurable settings (e.g. environment variable) to define whether it (or even the engine itself) accepts -Path or -LiteralPath by default when it is called from console/script-file, to give users more control?
Agreed re consistency, @kwkam.
The larger issue, however, is that _output_ file paths should never have been treated as wildcards to begin with, which takes us back to #4726, #6729, and also #9225
P.S.: The -FilePath parameters already are aliased to -Path:
Command Name Aliases
------- ---- -------
Export-FormatData Path {FilePath}
Invoke-Command FilePath {PSPath}
Out-File FilePath {Path}
Set-TraceSource FilePath {PSPath, Path}
Start-Process FilePath {PSPath, Path}
Tee-Object FilePath {Path}
Trace-Command FilePath {PSPath, Path}
Note that PSPath is yet another alias for some of them, which is also problematic, because PSPath should be the alias of -LiteralPath, to ensure treatment of pipeline input from, e.g., Get-ChildItem as the literal paths that they are.