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\$ Feb 19 at 4:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.