Note:
From the committee decision at #8587 (comment):
Filesystem accepting both / and \ on all systems is an explicit cross platform experience decision for PowerShell.
Given that it's currently impossible on Unix-like platforms - where \ is a legal filename character - to access or wildcard-match filenames with embedded \ chars., the following fixes are needed:
-
Wildcard matching must able to match \ chars., namely via ? or * or [\].
- Note that inside
[...], \ shouldn't require escaping, analogous to [.] matching a literal . in a regex.
-
-Path and -LiteralPath arguments must support `-escaping of \ chars. to request their literal treatment.
-
The .PS* provider property values (.PSPath, .PSParentPath, .PSChildName) must reflect the escaped path.
-
To-native-path conversions such as with Convert-Path must remove the escaping.
-
Native globbing must not break if a wildcard pattern happens to match a filename that contains \.
Note:
-
Even the above doesn't address the awkwardness of the - unescaped - .FullName value of System.IO.FileSystemInfo not being directly usable with the filesystem provider; e.g., with the provider bug fixed, something like (Get-ChildItem "$HOME/a\b").FullName would return something like /Users/jdoe/a\b - unescaped.
-
Similarly, native paths from outside sources with embedded \ - e.g., read from a file - will break in PowerShell commands, unless explicit escaping with -replace '`', '\`' is performed.
-
` as the escape char. means that it requires doubling inside "..." and therefore also in unquoted arguments in order to be passed through; i.e., the following calls would be equivalent and match a file literally named a\b:
Get-Item 'a`\b'
Get-Item "a``\b" # !!
Get-Item a``\b # !! implicitly treated like a "..." string
Steps to reproduce
On macOS or Linux, run the following Pester tests:
# Note: -Skip isn't yet supported at the level of Describe and Context blocks.
# See https://github.com/pester/Pester/issues/442
if ($env:OS -eq 'Windows_NT') {
Write-Warning "Tests apply to Unix-like platforms only" }
else {
Describe "Support for backslashes in Unix filenames" {
BeforeAll {
Push-Location TestDrive:\
# File with literal backslash in its name.
touch 'a\b'
$nativePath = ($PWD.ProviderPath -replace '/$') + '/' + 'a\b'
$escapedName = 'a`\b'
$escapedPath = ((Get-Item .).PSPath -replace '/$') + '/' + $escapedName
# File with literal backtick in its name.
touch 'a`b'
}
It "Backslashes can be matched with wildcards" {
(Get-Item -Path 'a?b').Name | Should -BeExactly 'a\b'
(Get-Item -Path 'a*b').Name | Should -BeExactly 'a\b'
(Get-Item -Path 'a[\]b').Name | Should -BeExactly 'a\b'
}
It "Backtick-escaped backslashes match literal ones in filenames" {
(Get-Item -Path 'a`\b').Name | Should -BeExactly 'a\b'
(Get-Item -LiteralPath 'a`\b').Name | Should -BeExactly 'a\b'
}
It "Backticks not preceding a backslash or backtick are literals in literal paths" {
(Get-Item -LiteralPath 'a`b').Name | Should -BeExactly 'a`b'
}
It "Doubled backticks are also literals in literal paths" {
(Get-Item -LiteralPath 'a``b').Name | Should -BeExactly 'a`b'
}
It "Backtick-escaped backticks must be recognized as a literal backtick in wildcard paths" {
(Get-Item -Path 'a``?').Name | Should -BeExactly 'a`b'
}
It "Provider properties contain escaped paths" {
(Get-Item -LiteralPath 'a`\b').PSChildName | Should -BeExactly $escapedName
(Get-Item -LiteralPath 'a`\b').PSPath | Should -BeExactly $escapedPath
}
It "Convert-Path removes the PS-specific escaping" {
Get-Item -LiteralPath 'a`\b' | Convert-Path | Should -BeExactly $nativePath
}
It "Native globbing doesn't break when a filename with a backslash is among the matches" {
/bin/echo a* | Should -Not -Match '\*'
}
AfterAll {
Pop-Location
}
}
}
Expected behavior
All tests should pass.
Actual behavior
All tests except "Backticks not preceding a backslash or backtick are literals in literal paths" fail.
Environment data
PowerShell Core 6.2.0-rc.1
Note:
-LiteralPathwhen it comes to backslashes #8158 and Should we do path normalizations on Unix well as on Windows? #6833 and "Rogue" filenames that contain a backslash break automatic globbing for native commands on macOS and Linux #10682 .From the committee decision at #8587 (comment):
Given that it's currently impossible on Unix-like platforms - where
\is a legal filename character - to access or wildcard-match filenames with embedded\chars., the following fixes are needed:Wildcard matching must able to match
\chars., namely via?or*or[\].[...],\shouldn't require escaping, analogous to[.]matching a literal.in a regex.-Pathand-LiteralPatharguments must support`-escaping of\chars. to request their literal treatment.The
.PS*provider property values (.PSPath,.PSParentPath,.PSChildName) must reflect the escaped path.To-native-path conversions such as with
Convert-Pathmust remove the escaping.Native globbing must not break if a wildcard pattern happens to match a filename that contains
\.Note:
Even the above doesn't address the awkwardness of the - unescaped -
.FullNamevalue ofSystem.IO.FileSystemInfonot being directly usable with the filesystem provider; e.g., with the provider bug fixed, something like(Get-ChildItem "$HOME/a\b").FullNamewould return something like/Users/jdoe/a\b- unescaped.Similarly, native paths from outside sources with embedded
\- e.g., read from a file - will break in PowerShell commands, unless explicit escaping with-replace '`', '\`'is performed.`as the escape char. means that it requires doubling inside"..."and therefore also in unquoted arguments in order to be passed through; i.e., the following calls would be equivalent and match a file literally nameda\b:Handling
`instances that precede a char. other than\gets quite tricky:Literal paths: So as not to break the existing ability to match
`literally in paths,`must be special only before\and before`itself. This follows the model of\use in double-quoted strings in Bash, but introduces the awkwardness of needing to interpret'a`b'and'ab' `` the same (similar to how"a\b"and `"a\b"` are equal in Bash).Wildcard paths: Wildcard matching itself always requires doubling of
`for literal use; e.g.,'a`b' -like 'a`b'is$false, and only'a`b' -like 'a``b'is$true(edge case:'a`' -like 'a`'is also$true- the trailing syntactically incomplete use of`is interpreted as a literal).Get-Item -Path 'a`b'match a file literally nameda`b, even though it shouldn't.Get-Item -Path 'ab'and neither withGet-Item -Path 'a?', even though it should.-Pathis the default parameter, this should be fixed, separately.Steps to reproduce
On macOS or Linux, run the following Pester tests:
Expected behavior
All tests should pass.
Actual behavior
All tests except "Backticks not preceding a backslash or backtick are literals in literal paths" fail.
Environment data