1
\$\begingroup\$

I'm trying to find all unique file owners in an NTFS file system. Getting the number is fairly straightforward, but my current approac is very inefficient:

$Shares = @( "\\Server\Share1", "\\Server\Share2", "\\Server\Share3", "\\Server2\Share1", ... ) $i = 0 foreach ($Share in $Shares) { $i++ $AllFiles = Get-ChildItem -Path $Share -File -Recurse -Force -ErrorAction SilentlyContinue $Output = $AllFiles | ForEach-Object { [System.IO.FileSystemAclExtensions]::GetAccessControl($_).Owner } | Group-Object | Sort-Object Name | Select-Object Count, Name $Output | Out-File "C:\My Path\My file$i.csv" } 

I don't think there's any way around using [System.IO.FileSystemAclExtensions]::GetAccessControl() but is there some better, more efficient way of getting the unique file owners only? There are two issues I can see here:

  1. Storing the file info of every single file in the share in a single variable
  2. Running a Group-Object command on that massive data set

I thought about instead of using Group-Object storing them in a separate ArrayList and for each file see if the owner already exists but then I'd have to compare an increasingly large ArrayList with a value every time, so that feels like it would be exponentially slower than doing the Group-Object approach.

Are there better options doing this?

\$\endgroup\$

    1 Answer 1

    4
    \$\begingroup\$

    If you only care about the unique file owners (not the count), something like (not fully fleshed out but to give you an idea):

    function Get-UniqueOwners([String]$Path) { $GciArgs = @{ LiteralPath=$Path; Recurse=$True; Force=$True; ErrorAction='SilentlyContinue'; } $Owners = New-Object System.Collections.Generic.HashSet[String] foreach ($file in Get-ChildItem @GciArgs | Where-Object {! $_.PSIsContainer}) { #"File: '{0}'" -f $file.FullName [void]$Owners.Add($file.GetAccessControl().Owner) } $Owners } $Shares = @( "\\Server\Share1", "\\Server\Share2", ) foreach ($Share in $Shares) { "Scanning '{0}'" -f $Share Get-UniqueOwners $Share "" } 

    Edit: or to get the owner count

    function Get-OwnerCount([String]$Path) { $GciArgs = @{ LiteralPath=$Path; Recurse=$True; Force=$True; ErrorAction='SilentlyContinue'; } $Owners = @{} foreach ($file in Get-ChildItem @GciArgs | Where-Object {! $_.PSIsContainer}) { $Owners[$file.GetAccessControl().Owner]++ } $Owners } 
    \$\endgroup\$
    1
    • \$\begingroup\$I tried this back and forth but unfortunately I can't see any performance difference between using the Group-Object approach and this :( I assume that this is because the vast majority of the time is spent reading the owner information with .GetAccessControl() and that the time saving from Group-Object is comparatively negligeble. Although I do admit I haven't tried this in production yet so it could still be the case that this approach is somewhat faster should the Group-Object start taking hours. I will update when I have verified this!\$\endgroup\$CommentedFeb 19, 2024 at 4:06

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.