如何用PowerShell结合RoboCopy批量分移文件至固定数量子文件夹
优化批量文件拆分移动脚本:用RoboCopy替代Move-Item提升性能
我正在编写PowerShell脚本,将目标文件夹中的文件按动态指定数量n拆分到子文件夹中。例如,若Folder A有9000个文件,设置每文件夹1000个文件,脚本会在Folder A内创建9个子文件夹,各含1000个文件。
原脚本使用Move-Item实现文件移动,但在生产环境处理数千文件时存在严重性能瓶颈。尝试集成RoboCopy解决性能问题,但遇到难题:需移动的文件存储在列表中,无法直接使用RoboCopy.exe C:\Source C:\Destination /mov的常规命令。需要实现多线程支持的高效文件移动。
修改后的完整脚本
param ( [Parameter(Mandatory,Position=0)] [String] $FileList, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName)] [Int32] $NumFilesPerFolder = 1000, [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName)] [Int32] $FolderNumberPadding = 2, # RoboCopy多线程数,默认8,可根据系统资源调整 [Parameter(Mandatory=$false,ValueFromPipelineByPropertyName)] [Int32] $RoboCopyThreadCount = 8 ) $Folders = Get-Content $FileList Set-Location -LiteralPath ([IO.Path]::GetTempPath()) function Move-Files { [CmdletBinding()] param ( [Parameter(Mandatory,Position=0)] [System.Collections.ArrayList] $List, [Parameter(Mandatory)] [Int32] $Index, [Parameter(Mandatory)] [Int32] $ThreadCount ) $BaseFolder = [System.IO.Path]::GetDirectoryName($List[0]) $DestFolderName = $Index.ToString().PadLeft($FolderNumberPadding, '0') $DestFolder = New-Item -Path (Join-Path $BaseFolder $DestFolderName) -Type Directory -Force # 创建临时文件存储要移动的文件全路径 $tempFile = [IO.Path]::GetTempFileName() $List.FullName | Out-File -FilePath $tempFile -Encoding utf8 -Force # 调用RoboCopy执行多线程移动 $roboCopyArgs = @( $BaseFolder, $DestFolder.FullName, '/COPYALL', # 复制所有文件属性(权限、时间戳等) '/MOV', # 移动文件(复制后删除源文件) "/MT:$ThreadCount", # 启用多线程 '/R:0', # 失败重试次数(设为0避免等待) '/W:0', # 重试间隔(设为0) '/XF', '*', # 排除所有文件 '/IF', "@$tempFile" # 仅包含临时文件列表中的文件 ) & robocopy.exe $roboCopyArgs | Out-Null # 清理临时文件 Remove-Item -LiteralPath $tempFile -Force } foreach ($Folder in $Folders) { $Files = Get-ChildItem -LiteralPath $Folder -File -Force $filesidx = 1 $totalidx = $null $groupidx = 0 $FilesToMove = [System.Collections.ArrayList]@() foreach ($File in $Files) { if($null -eq $totalidx){ $totalidx = $Files.Length } if($filesidx -eq 1){ $groupidx++ } $FilesToMove.Add($File) | Out-Null if($filesidx -eq $NumFilesPerFolder){ Move-Files -List $FilesToMove -Index $groupidx -ThreadCount $RoboCopyThreadCount $FilesToMove.Clear() $filesidx = 1 }elseif($totalidx -eq 1){ Move-Files -List $FilesToMove -Index $groupidx -ThreadCount $RoboCopyThreadCount $FilesToMove.Clear() break }else{ $filesidx++ } $totalidx-- } } Remove-Item $FileList -Force $app = New-Object -ComObject Shell.Application $appwin = $app.Windows() foreach ($window in $appwin) { if($window.Name -eq "File Explorer"){ $window.Refresh() } } Invoke-VBMessageBox "Operation Complete" -Title "Operation Complete" -Icon Information -BoxType OKOnly
关键改动说明
- 新增RoboCopy线程数参数:添加
$RoboCopyThreadCount参数,允许根据系统资源调整多线程数量(默认8) - 临时文件存储文件列表:将待移动文件的全路径写入临时文件,供RoboCopy通过
/IF @<文件>参数读取,解决无法直接传递文件列表的问题 - RoboCopy参数优化:
/COPYALL:保留文件所有属性(权限、时间戳等),确保移动后文件属性与源文件一致/MOV:实现移动操作(复制完成后删除源文件)/MT:$ThreadCount:启用多线程移动,大幅提升批量文件处理速度/R:0和/W:0:取消失败重试等待,避免不必要的延迟/XF *配合/IF @<文件>:仅移动列表中指定的文件,排除其他文件
- 清理临时文件:每次移动完成后删除临时文件,避免磁盘残留
内容的提问来源于stack exchange,提问作者fmotion1
相关产品推荐
相关产品推荐

