Browse Source

fix: treat brackets in the filter of Get-ChildItem2 as characters

The cmdlet compares the name of every item that the enumeration returns with the pattern again, and it built that comparison with the wildcard syntax of PowerShell. A bracket then began a character class, so Report[1].txt was returned by the enumeration, which treats a bracket as itself, and dropped by the comparison, and a file with brackets in its name could not be found for its name with -Filter, which Get-ChildItem does. The documentation names only * and ? as wildcards, so a bracket and a backtick now stand for themselves in the comparison.

The regression test fails without the fix in all four configurations. The probe also showed that AlphaFS compares only the long name, so the test for *.htm guards the documented contract and no 8.3 behavior. The page of the cmdlet names the rule and the help file is generated again.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/118/head
Raimund Andree 2 days ago
parent
commit
ee7c105d40
  1. 3
      CHANGELOG.md
  2. 4
      Docs/Cmdlets/Get-ChildItem2.md
  3. 5
      NTFSSecurity/ItemCmdlets/GetChildItem2.cs
  4. 5
      NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml
  5. 22
      Tests/ItemCmdlets.Tests.ps1

3
CHANGELOG.md

@ -130,6 +130,9 @@ The format is based on
the pipeline: a `break` or `continue` or `Select-Object -First` became an 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` error of the item, so that `Remove-Item2 -PassThru | Select-Object -First 1`
removed every item. They now stop and write no error 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 - Fix `Get-Help`, which showed only the syntax: ship the help file
`en-US\NTFSSecurity.dll-Help.xml` generated from the cmdlet documentation, `en-US\NTFSSecurity.dll-Help.xml` generated from the cmdlet documentation,
including the links that `Get-Help -Online` opens, instead of the outdated including the links that `Get-Help -Online` opens, instead of the outdated

4
Docs/Cmdlets/Get-ChildItem2.md

@ -134,7 +134,7 @@ Accept wildcard characters: False
### -Filter ### -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 ```yaml
Type: String 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, 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 ## RELATED LINKS
[Get-Item2](Get-Item2.md) [Get-Item2](Get-Item2.md)

5
NTFSSecurity/ItemCmdlets/GetChildItem2.cs

@ -145,7 +145,10 @@ namespace NTFSSecurity
paths = new List<string>() { GetCurrentLocation() }; paths = new List<string>() { 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"); modeMethodInfo = typeof(FileSystemCodeMembers).GetMethod("Mode");

5
NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml

@ -3520,7 +3520,7 @@ PS C:\&gt; Disable-Privileges</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none"> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<maml:name>Filter</maml:name> <maml:name>Filter</maml:name>
<maml:description> <maml:description>
<maml:para>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.</maml:para> <maml:para>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.</maml:para>
</maml:description> </maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue> <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type> <dev:type>
@ -3724,7 +3724,7 @@ PS C:\&gt; Disable-Privileges</dev:code>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none"> <command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="2" aliases="none">
<maml:name>Filter</maml:name> <maml:name>Filter</maml:name>
<maml:description> <maml:description>
<maml:para>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.</maml:para> <maml:para>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.</maml:para>
</maml:description> </maml:description>
<command:parameterValue required="true" variableLength="false">String</command:parameterValue> <command:parameterValue required="true" variableLength="false">String</command:parameterValue>
<dev:type> <dev:type>
@ -3866,6 +3866,7 @@ PS C:\&gt; Disable-Privileges</dev:code>
<maml:para>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.</maml:para> <maml:para>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.</maml:para>
<maml:para>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.</maml:para> <maml:para>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.</maml:para>
<maml:para>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.</maml:para> <maml:para>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.</maml:para>
<maml:para>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.</maml:para>
</maml:alert> </maml:alert>
</maml:alertSet> </maml:alertSet>
<command:examples> <command:examples>

22
Tests/ItemCmdlets.Tests.ps1

@ -140,9 +140,8 @@ Describe 'Get-ChildItem2' {
($relative | Sort-Object) -join ',' | Should -Be (($Expected | Sort-Object) -join ',') ($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 # The pattern must match the name of the item, not its short name (8.3), which Get-ChildItem in Windows PowerShell
# short name (8.3) of an item, so *.htm finds Page2.html as well, as Get-ChildItem does where the volume creates # also compares: there, *.htm returns Page2.html on a volume that creates short names.
# short names. The cmdlet compares the name again.
It 'Should return only the items whose name matches -Filter <Filter>' -ForEach @( It 'Should return only the items whose name matches -Filter <Filter>' -ForEach @(
@{ Filter = '*.htm'; Expected = @('Page.htm') } @{ Filter = '*.htm'; Expected = @('Page.htm') }
@{ Filter = 'Page?.html'; Expected = @('Page2.html') } @{ Filter = 'Page?.html'; Expected = @('Page2.html') }
@ -160,6 +159,23 @@ Describe 'Get-ChildItem2' {
($result.Name | Sort-Object) -join ',' | Should -Be (($Expected | Sort-Object) -join ',') ($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' { 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) $result = @(Get-ChildItem2 -Path $tree -Recurse -ErrorVariable childErrors -ErrorAction SilentlyContinue | Select-Object -First 1)

Loading…
Cancel
Save