如何优化扫描14TB DFS共享的PowerShell ACL脚本以提速?
调优PowerShell DFS文件夹权限扫描脚本以提升速度
我是PowerShell新手,若有错误请见谅。因一名资深员工突然离职,出于安全考虑,我被指派找出该员工在DFS中拥有访问权限的所有文件夹。找不到合适的脚本扫描14TB的DFS共享以排查该用户及其所属组的访问权限,于是自行编写了脚本。脚本可正常运行但速度过慢,希望能进行调优以提升运行速度。
我将脚本分为两部分执行:先保存所有文件夹路径,再遍历每个路径获取ACL权限并筛选目标用户/组信息,保存为以~为分隔符的CSV文件,使用PowerShell 5.1环境。原脚本如下:
$ErrorActionPreference = "Continue" #$rootDirectory = 'C:\temp' $rootDirectory = '\\?\UNC\myServer\myShare' $scriptName = 'myACL' $version = 1.0 $dateStamp = (Get-Date).ToString('yyyyMMddHHmm') $scriptDirectory = $PSScriptRoot $log = $scriptDirectory + "\" + $scriptName + "_dirList_v" + $version + "_"+$dateStamp+".log" "Path" | Out-File $log function getSubfolders ([String]$arg_directory, [string]$arg_log) { $subFolders = Get-ChildItem -LiteralPath $arg_directory -Directory -Force -ErrorAction SilentlyContinue | Select-Object -expandProperty FullName $subFolders | Out-File $arg_log -append foreach ($folder in $subFolders) { getSubfolders $folder $arg_log } } #part1 getSubfolders $rootDirectory $log #part2 $dirListSourceFile = $log $log2 = $scriptDirectory + "\" + $scriptName + "_permissionList_v" + $version + "_"+$dateStamp+".csv" $i=0 "Sr~Path~User/Group~Rights~isInherited?" | Out-File $log2 Start-Sleep -s 2 Import-CSV $dirListSourceFile | ForEach-Object{ $i++ $path = $_.path.Trim() $Acl = get-acl $path | Select * ForEach ($Access in $Acl.Access) { if($Access.IdentityReference.value -eq "mydomain\user1" -or $Access.IdentityReference.value -eq "mydomain\sg1" -or $Access.IdentityReference.value -eq "mydomain\sg2" -or $Access.IdentityReference.value -eq "mydomain\sg3" -or $Access.IdentityReference.value -eq "mydomain\sg4") { "$i~$path~$($Access.IdentityReference.value)~$($Access.FileSystemRights)~$($Access.IsInherited)" | Out-File $log2 -append } } }
调优方案
1. 合并两步操作,消除中间文件IO开销
原脚本先写入文件夹路径到日志再读取处理,两次磁盘IO会大幅拖慢速度。直接在遍历文件夹时同步处理ACL,减少不必要的文件操作。
2. 替换递归函数为Get-ChildItem -Recurse
自定义递归函数在处理海量文件夹时会产生额外的函数调用开销,Get-ChildItem -Recurse是原生优化的递归遍历方式,效率更高。
3. 批量写入结果,减少文件操作次数
频繁使用Out-File -Append会重复执行文件打开、写入、关闭操作,严重影响性能。先将结果存入内存数组,最后一次性写入文件。
4. 用集合存储目标身份,加快筛选速度
原脚本的多-or判断效率低下,改用数组集合的Contains方法,查找速度更快。
5. 简化Get-Acl调用
Get-Acl $path | Select *会创建冗余对象,直接使用Get-Acl返回的原始对象即可。
优化后的脚本
$ErrorActionPreference = "Continue" $rootDirectory = '\\?\UNC\myServer\myShare' $scriptName = 'myACL' $version = 1.0 $dateStamp = (Get-Date).ToString('yyyyMMddHHmm') $scriptDirectory = $PSScriptRoot $outputFile = Join-Path $scriptDirectory "$scriptName`_permissionList_v$version`_$dateStamp.csv" # 预定义需要排查的用户/组集合,提升筛选效率 $targetIdentities = @( "mydomain\user1", "mydomain\sg1", "mydomain\sg2", "mydomain\sg3", "mydomain\sg4" ) # 初始化结果数组,批量存储数据 $results = @() $results += "Sr~Path~User/Group~Rights~isInherited?" $i = 0 # 直接递归遍历并处理ACL,跳过中间文件 Get-ChildItem -LiteralPath $rootDirectory -Directory -Recurse -Force -ErrorAction SilentlyContinue | ForEach-Object { $i++ $path = $_.FullName.Trim() $acl = Get-Acl -LiteralPath $path foreach ($access in $acl.Access) { $identity = $access.IdentityReference.Value if ($targetIdentities.Contains($identity)) { $results += "$i~$path~$identity~$($access.FileSystemRights)~$($access.IsInherited)" } } } # 一次性写入所有结果 $results | Out-File -FilePath $outputFile -Encoding utf8
额外优化建议
- 并行处理:如果可以升级到PowerShell 7+,可使用
ForEach-Object -Parallel实现多线程遍历;若必须使用PowerShell 5.1,可通过RunspacePool手动实现并行处理,大幅提升大文件系统的扫描速度。 - 跳过继承权限:如果某文件夹的权限完全继承自已扫描的父文件夹,且没有显式权限条目,可以跳过该文件夹的ACL检查,减少重复工作(需注意:若有显式添加的权限仍需处理)。
内容的提问来源于stack exchange,提问作者Majid
相关产品推荐
相关产品推荐

