diff --git a/CHANGELOG.md b/CHANGELOG.md index fa997d1..b494b2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,9 @@ The format is based on the pipeline: a `break` or `continue` or `Select-Object -First` became an error of the item, so that `Remove-Item2 -PassThru | Select-Object -First 1` removed every item. They now stop and write no error +- Fix `Get-ChildItem2 -Filter`, which read a bracket as the start of a + character class, so that it did not return a file with brackets in its name, + such as `Report[1].txt`, for that name; only `*` and `?` are wildcards - Fix `Get-Help`, which showed only the syntax: ship the help file `en-US\NTFSSecurity.dll-Help.xml` generated from the cmdlet documentation, including the links that `Get-Help -Online` opens, instead of the outdated diff --git a/Docs/Cmdlets/Get-ChildItem2.md b/Docs/Cmdlets/Get-ChildItem2.md index b2e3519..c39bbaf 100644 --- a/Docs/Cmdlets/Get-ChildItem2.md +++ b/Docs/Cmdlets/Get-ChildItem2.md @@ -134,7 +134,7 @@ Accept wildcard characters: False ### -Filter -Specifies a name pattern that an item must match to be returned. The pattern supports the `*` and `?` wildcard characters, and the match ignores case. The default value is `*`, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. +Specifies a name pattern that an item must match to be returned. The pattern supports the `*` and `?` wildcard characters, and the match ignores case; any other character, such as a bracket, stands for itself, so `Report[1].txt` returns the file of that name. The default value is `*`, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. ```yaml Type: String @@ -309,6 +309,8 @@ A folder that cannot be read produces a non-terminating error with the ID `DirUn Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `InvalidCastException`, `-Attributes` returned only the items that had all the listed attributes, and an empty `-Attributes` value returned every item, also the hidden ones. Earlier builds, including the 5.0.0 prereleases, could also omit the first hidden item with `-Hidden` unless `-Force` was explicitly supplied. +Before 5.0.0, `-Filter` read a bracket as the start of a character class, so a file with brackets in its name, such as `Report[1].txt`, was not returned for its name, and a `break` or `continue` in a later command of the pipeline did not end the cmdlet for an item below the first folder. + ## RELATED LINKS [Get-Item2](Get-Item2.md) diff --git a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs index ac8a3da..58b126d 100644 --- a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs +++ b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs @@ -145,7 +145,10 @@ namespace NTFSSecurity paths = new List() { GetCurrentLocation() }; } - wildcard = new WildcardPattern(filter, WildcardOptions.Compiled | WildcardOptions.IgnoreCase); + // Only * and ? are wildcards, like in the pattern that the enumeration matches; a bracket or a backtick stands + // for itself. Before 5.0.0, [1] was read as a character class, so a file with brackets in its name was not + // returned for its name. + wildcard = new WildcardPattern(filter.Replace("`", "``").Replace("[", "`[").Replace("]", "`]"), WildcardOptions.Compiled | WildcardOptions.IgnoreCase); modeMethodInfo = typeof(FileSystemCodeMembers).GetMethod("Mode"); diff --git a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml index cce32d4..276123f 100644 --- a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml +++ b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml @@ -3520,7 +3520,7 @@ PS C:\> Disable-Privileges Filter - Specifies a name pattern that an item must match to be returned. The pattern supports the ` ` and `?` wildcard characters, and the match ignores case. The default value is ` `, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. + Specifies a name pattern that an item must match to be returned. The pattern supports the ` ` and `?` wildcard characters, and the match ignores case; any other character, such as a bracket, stands for itself, so `Report[1].txt` returns the file of that name. The default value is ` `, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. String @@ -3724,7 +3724,7 @@ PS C:\> Disable-Privileges Filter - Specifies a name pattern that an item must match to be returned. The pattern supports the ` ` and `?` wildcard characters, and the match ignores case. The default value is ` `, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. + Specifies a name pattern that an item must match to be returned. The pattern supports the ` ` and `?` wildcard characters, and the match ignores case; any other character, such as a bracket, stands for itself, so `Report[1].txt` returns the file of that name. The default value is ` `, which returns every item. The pattern is applied to the name of each item, not to its path, and during a recursive listing it restricts only the returned items; the cmdlet still descends into every subfolder. String @@ -3866,6 +3866,7 @@ PS C:\> Disable-Privileges The `PrivateData` section of the module manifest `NTFSSecurity.psd1` contains two settings that this cmdlet reads when it starts. `GetFileSystemModeProperty` adds the calculated `Mode` property to every item. `IdentifyHardLinks` adds the `HardLinkCount` property to every file, which requires an extra call into the file system for each file and therefore slows down large listings noticeably. Set either value to `$false` in the manifest and import the module again if you prefer the faster enumeration over the additional properties. A folder that cannot be read produces a non-terminating error with the ID `DirUnauthorizedAccessError` for an access denial or `DirUnspecifiedError` for any other failure, and a path that does not exist produces the error `FileNotFound`. In each case the cmdlet continues with the next path. Failures that occur while `-Recurse` collects the subfolders of a folder are reported as verbose messages only, not as errors. Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `InvalidCastException`, `-Attributes` returned only the items that had all the listed attributes, and an empty `-Attributes` value returned every item, also the hidden ones. Earlier builds, including the 5.0.0 prereleases, could also omit the first hidden item with `-Hidden` unless `-Force` was explicitly supplied. + Before 5.0.0, `-Filter` read a bracket as the start of a character class, so a file with brackets in its name, such as `Report[1].txt`, was not returned for its name, and a `break` or `continue` in a later command of the pipeline did not end the cmdlet for an item below the first folder. diff --git a/Tests/ItemCmdlets.Tests.ps1 b/Tests/ItemCmdlets.Tests.ps1 index 8458488..9e22b86 100644 --- a/Tests/ItemCmdlets.Tests.ps1 +++ b/Tests/ItemCmdlets.Tests.ps1 @@ -140,9 +140,8 @@ Describe 'Get-ChildItem2' { ($relative | Sort-Object) -join ',' | Should -Be (($Expected | Sort-Object) -join ',') } - # The pattern must match the name of the item. When Windows lists a folder with a pattern, it also compares the - # short name (8.3) of an item, so *.htm finds Page2.html as well, as Get-ChildItem does where the volume creates - # short names. The cmdlet compares the name again. + # The pattern must match the name of the item, not its short name (8.3), which Get-ChildItem in Windows PowerShell + # also compares: there, *.htm returns Page2.html on a volume that creates short names. It 'Should return only the items whose name matches -Filter ' -ForEach @( @{ Filter = '*.htm'; Expected = @('Page.htm') } @{ Filter = 'Page?.html'; Expected = @('Page2.html') } @@ -160,6 +159,23 @@ Describe 'Get-ChildItem2' { ($result.Name | Sort-Object) -join ',' | Should -Be (($Expected | Sort-Object) -join ',') } + # Only * and ? are wildcards in -Filter. A bracket stands for itself, so a file with brackets in its name is found + # by its name, as Get-ChildItem finds it, and the file that the brackets would select as a character class is not. + # Before 5.0.0, the cmdlet read [1] as a character class and returned nothing. + It 'Should find a file whose name contains brackets by that name with -Filter' { + $folder = New-TestSandboxItem -Sandbox $sandbox -Name 'FilterBrackets' -Directory + foreach ($name in 'Report[1].txt', 'Report1.txt') { + $file = Join-Path -Path $folder -ChildPath $name + Assert-TestSandboxPath -Sandbox $sandbox -Path $file + Set-Content -LiteralPath $file -Value $name + } + + $result = @(Get-ChildItem2 -Path $folder -Filter 'Report[1].txt' -ErrorAction Stop) + + $result | Should -HaveCount 1 + $result[0].Name | Should -BeExactly 'Report[1].txt' + } + It 'Should stop a recursive pipeline without recording an enumeration error' { $result = @(Get-ChildItem2 -Path $tree -Recurse -ErrorVariable childErrors -ErrorAction SilentlyContinue | Select-Object -First 1)