From 55be4d7842ed3d45d58752c52c029e135ec3f6b3 Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Mon, 5 Oct 2026 08:09:16 +0200 Subject: [PATCH] feat!: match any listed attribute in Get-ChildItem2 -Attributes Get-ChildItem2 -Attributes returned only the items that had all the listed attributes, so -Attributes Hidden, ReadOnly returned nothing where Get-ChildItem returns both kinds of items. It now returns the items that have any of them (#5). BREAKING CHANGE: to get only the items with all the listed attributes, filter the result with Where-Object, as the cmdlet page shows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- CHANGELOG.md | 5 ++++ Docs/Cmdlets/Get-ChildItem2.md | 8 +++--- NTFSSecurity/ItemCmdlets/GetChildItem2.cs | 3 ++- NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml | 10 ++++---- Tests/ItemCmdlets.Tests.ps1 | 26 ++++++++++++++++++++ 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06aeafb..4814538 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,11 @@ The format is based on before. To remove the entries, use `Disable-NTFSAccessInheritance -RemoveInheritedAccessRules` or `Enable-NTFSAuditInheritance -RemoveExplicitAuditRules` +- **Breaking:** `Get-ChildItem2 -Attributes` returns the items that have any + of the listed attributes, like `Get-ChildItem`; it returned only the items + that had all of them. To get the old result, filter with `Where-Object`, + as the cmdlet page shows + ([#5](https://github.com/raandree/NTFSSecurity/issues/5)) ### Deprecated diff --git a/Docs/Cmdlets/Get-ChildItem2.md b/Docs/Cmdlets/Get-ChildItem2.md index 5444ce2..0c43b87 100644 --- a/Docs/Cmdlets/Get-ChildItem2.md +++ b/Docs/Cmdlets/Get-ChildItem2.md @@ -57,19 +57,19 @@ PS C:\> Get-ChildItem2 -Path C:\Data -Recurse -Depth 1 -Filter '*.log' Returns the log files in `C:\Data` and in its immediate subfolders. Without `-Depth`, the command would descend through the entire tree. -### Example 4: List hidden system files +### Example 4: List hidden or system files ```PowerShell PS C:\> dir2 -Path C:\Data -Attributes Hidden, System ``` -Uses the `dir2` alias and returns the items of `C:\Data` that have both the hidden and the system attribute. +Uses the `dir2` alias and returns the items of `C:\Data` that have the hidden or the system attribute, like `Get-ChildItem -Attributes Hidden, System`. ## PARAMETERS ### -Attributes -Specifies a set of file attributes. The cmdlet returns only the items that have all the attributes you list; separate several values with commas, as in `-Attributes Hidden, System`. When you use this parameter, the cmdlet ignores `-Force`, `-Hidden`, `-System`, and `-ReadOnly`, and it returns matching hidden items without `-Force`. +Specifies a set of file attributes. Like `Get-ChildItem`, the cmdlet returns the items that have at least one of the attributes you list; separate several values with commas, as in `-Attributes Hidden, System`. To get only the items that have all of them, filter the result, for example with `Where-Object { ($_.Attributes -band [IO.FileAttributes]'Hidden, System') -eq [IO.FileAttributes]'Hidden, System' }`. When you use this parameter, the cmdlet ignores `-Force`, `-Hidden`, `-System`, and `-ReadOnly`, and it returns matching hidden items without `-Force`. ```yaml Type: FileAttributes @@ -307,7 +307,7 @@ The `PrivateData` section of the module manifest `NTFSSecurity.psd1` contains tw 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`. +Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `InvalidCastException`, and `-Attributes` returned only the items that had all the listed attributes. ## RELATED LINKS diff --git a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs index 70aaa2b..246df25 100644 --- a/NTFSSecurity/ItemCmdlets/GetChildItem2.cs +++ b/NTFSSecurity/ItemCmdlets/GetChildItem2.cs @@ -275,7 +275,8 @@ namespace NTFSSecurity if (MyInvocation.BoundParameters.ContainsKey("Attributes")) { - if ((current.Attributes & attributes) != attributes) + // Like Get-ChildItem, an item matches when it has any of the listed attributes (#5). + if (attributes != 0 && (current.Attributes & attributes) == 0) continue; writeItem = true; diff --git a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml index 888514c..e8d1d7b 100644 --- a/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml +++ b/NTFSSecurity/en-US/NTFSSecurity.dll-Help.xml @@ -3524,7 +3524,7 @@ PS C:\> Disable-Privileges Attributes - Specifies a set of file attributes. The cmdlet returns only the items that have all the attributes you list; separate several values with commas, as in `-Attributes Hidden, System`. When you use this parameter, the cmdlet ignores `-Force`, `-Hidden`, `-System`, and `-ReadOnly`, and it returns matching hidden items without `-Force`. + Specifies a set of file attributes. Like `Get-ChildItem`, the cmdlet returns the items that have at least one of the attributes you list; separate several values with commas, as in `-Attributes Hidden, System`. To get only the items that have all of them, filter the result, for example with `Where-Object { ($_.Attributes -band [IO.FileAttributes]'Hidden, System') -eq [IO.FileAttributes]'Hidden, System' }`. When you use this parameter, the cmdlet ignores `-Force`, `-Hidden`, `-System`, and `-ReadOnly`, and it returns matching hidden items without `-Force`. ReadOnly @@ -3668,7 +3668,7 @@ PS C:\> Disable-Privileges Attributes - Specifies a set of file attributes. The cmdlet returns only the items that have all the attributes you list; separate several values with commas, as in `-Attributes Hidden, System`. When you use this parameter, the cmdlet ignores `-Force`, `-Hidden`, `-System`, and `-ReadOnly`, and it returns matching hidden items without `-Force`. + Specifies a set of file attributes. Like `Get-ChildItem`, the cmdlet returns the items that have at least one of the attributes you list; separate several values with commas, as in `-Attributes Hidden, System`. To get only the items that have all of them, filter the result, for example with `Where-Object { ($_.Attributes -band [IO.FileAttributes]'Hidden, System') -eq [IO.FileAttributes]'Hidden, System' }`. When you use this parameter, the cmdlet ignores `-Force`, `-Hidden`, `-System`, and `-ReadOnly`, and it returns matching hidden items without `-Force`. FileAttributes @@ -3857,7 +3857,7 @@ PS C:\> Disable-Privileges The default table view shows the `Mode`, `Inherits`, `LastWriteTime`, `Size(M)`, and `Name` columns. `Inherits` is `False` for an item whose access inheritance is disabled. Reading that value costs one access to the ACL of each displayed item, which slows down the display of large listings; to avoid it, select the properties you need, for example with `Format-Table -Property Mode, LastWriteTime, Length, Name`. Objects that you pipe to another command are not affected. Before 5.0.0, the column showed `True` for every item. 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`. + Before 5.0.0, a `-Path` value that points to a file stopped the cmdlet with an `InvalidCastException`, and `-Attributes` returned only the items that had all the listed attributes. @@ -3883,10 +3883,10 @@ PS C:\> Disable-Privileges - ------------- Example 4: List hidden system files ------------- + ------------ Example 4: List hidden or system files ------------ PS C:\> dir2 -Path C:\Data -Attributes Hidden, System - Uses the `dir2` alias and returns the items of `C:\Data` that have both the hidden and the system attribute. + Uses the `dir2` alias and returns the items of `C:\Data` that have the hidden or the system attribute, like `Get-ChildItem -Attributes Hidden, System`. diff --git a/Tests/ItemCmdlets.Tests.ps1 b/Tests/ItemCmdlets.Tests.ps1 index 336091a..1308cd8 100644 --- a/Tests/ItemCmdlets.Tests.ps1 +++ b/Tests/ItemCmdlets.Tests.ps1 @@ -78,6 +78,32 @@ Describe 'Get-ChildItem2' { ($lines | Where-Object -FilterScript { $_ -match 'Inheriting\.txt\s*$' }) | Should -Match '\bTrue\b' } } + + Context 'With -Attributes' { + BeforeAll { + $attributeFolder = New-TestSandboxItem -Sandbox $sandbox -Name 'Attributes' -Directory + $hiddenFile = Join-Path -Path $attributeFolder -ChildPath 'Hidden.txt' + $readOnlyFile = Join-Path -Path $attributeFolder -ChildPath 'ReadOnly.txt' + $plainFile = Join-Path -Path $attributeFolder -ChildPath 'Plain.txt' + Assert-TestSandboxPath -Sandbox $sandbox -Path $hiddenFile, $readOnlyFile, $plainFile + Set-Content -LiteralPath $hiddenFile, $readOnlyFile, $plainFile -Value 'Attributes' + (Get-Item -LiteralPath $hiddenFile -Force).Attributes = [IO.FileAttributes]::Hidden + (Get-Item -LiteralPath $readOnlyFile).Attributes = [IO.FileAttributes]::ReadOnly + } + + # Before 5.0.0, the cmdlet returned only the items that had all the listed attributes (#5). + It 'Should return the items that have any of the listed attributes, like Get-ChildItem' { + $result = @(Get-ChildItem2 -Path $attributeFolder -Attributes Hidden, ReadOnly) + + @($result.Name | Sort-Object) | Should -Be @('Hidden.txt', 'ReadOnly.txt') + } + + It 'Should return only the items with the attribute when one is listed' { + $result = @(Get-ChildItem2 -Path $attributeFolder -Attributes ReadOnly) + + $result.Name | Should -Be 'ReadOnly.txt' + } + } } Describe 'Copy-Item2, Move-Item2, and Remove-Item2 with several paths' {