如何用PowerShell持续将TEMP文件夹文件复制到指定目录?
Windows Server 2019中FileSystemWatcher自动复制TEMP文件问题修复
问题场景
在Windows Server 2019上尝试通过PowerShell的FileSystemWatcher持续监控C:\Users\Administrator\AppData\Local\Temp文件夹,将新创建或修改的文件自动复制到C:\Users\Administrator\copied目录,但运行代码后出现大量“无文件路径、文件不存在或事件类型不支持”的错误,且复制操作未正常执行。
原代码
# Define the source and destination directories $sourceDirectory = 'C:\Users\Administrator\AppData\Local\Temp' $destinationDirectory = 'C:\Users\Administrator\copied' # Create an instance of the FileSystemWatcher class $watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = $sourceDirectory $watcher.IncludeSubdirectories = $true # Specify the type of changes to monitor $watcher.NotifyFilter = [System.IO.NotifyFilters]'FileName, LastWrite' # Filter the file types to monitor $watcher.Filter = "*.*" # Define the action to take when a file is created or modified $action = { param($sender, $eventArgs) $destinationDirectory = 'C:\Users\Administrator\copied' $eventType = $eventArgs.ChangeType $filePath = $eventArgs.FullPath if ($filePath -and (Test-Path $filePath) -and ($eventType -eq 'Created' -or $eventType -eq 'Changed')) { # Construct the destination file path $destFilePath = Join-Path -Path $destinationDirectory -ChildPath (Split-Path -Path $filePath -Leaf) # Copy the file to the destination directory try { Copy-Item -Path $filePath -Destination $destFilePath -Force } catch { Write-Host ("Failed to copy file. Error: " + $_.Exception.Message) } } else { Write-Host "Event detected with no file path, or the file does not exist, or the event type is not supported. Ignoring." Write-Host "Full event details: $eventArgs" } } # Add the event handlers for the Created and Changed events Register-ObjectEvent -InputObject $watcher -EventName 'Created' -Action $action Register-ObjectEvent -InputObject $watcher -EventName 'Changed' -Action $action # Start monitoring the files $watcher.EnableRaisingEvents = $true # Keep the PowerShell console open while ($true) { Start-Sleep -Seconds 1 }
错误信息
Id Name PSJobTypeName State HasMoreData Location Command -- ---- ------------- ----- ----------- -------- ------- 31 aa937f11-a26... NotStarted False ... 32 2db275b2-991... NotStarted False ... Event detected with no file path, ignoring. Full event details: System.IO.FileSystemEventArgs Event detected with no file path, or the file does not exist, or the event type is not supported. Ignoring. Full event details: System.IO.FileSystemEventArgs Event detected with no file path, or the file does not exist, or the event type is not supported. Ignoring. Full event details: System.IO.FileSystemEventArgs
问题原因分析
- 文件写入未完成:当
Created或Changed事件触发时,文件可能仍被其他进程写入,此时Test-Path会返回false,导致跳过复制。 - Changed事件重复触发:
FileSystemWatcher的Changed事件会因文件元数据(如修改时间)变化多次触发,部分触发时文件状态不稳定。 - 无文件锁定处理:TEMP目录的文件常被进程占用,直接执行
Copy-Item会因文件锁定失败,且无重试逻辑。
修复方案
- 增加文件存在性检查的重试逻辑,等待文件写入完成。
- 针对
Changed事件,仅处理文件内容变更,过滤元数据变化。 - 为
Copy-Item添加重试机制,应对文件锁定情况。 - 优化事件参数处理,确保
$filePath有效。
修正后的代码
# 定义源目录和目标目录 $sourceDirectory = 'C:\Users\Administrator\AppData\Local\Temp' $destinationDirectory = 'C:\Users\Administrator\copied' # 创建目标目录(如果不存在) if (-not (Test-Path -Path $destinationDirectory)) { New-Item -Path $destinationDirectory -ItemType Directory | Out-Null } # 初始化FileSystemWatcher $watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = $sourceDirectory $watcher.IncludeSubdirectories = $true $watcher.NotifyFilter = [System.IO.NotifyFilters]'FileName, LastWrite, Size' $watcher.Filter = "*.*" # 定义事件处理动作 $action = { param($sender, $eventArgs) $destinationDirectory = 'C:\Users\Administrator\copied' $eventType = $eventArgs.ChangeType $filePath = $eventArgs.FullPath $maxRetries = 5 $retryDelay = 200 # 毫秒 # 仅处理Created和Changed事件,且确保文件路径有效 if (-not $filePath -or ($eventType -notin @('Created', 'Changed'))) { Write-Host "无效事件或路径,忽略。事件类型: $eventType,路径: $filePath" return } # 重试检查文件是否存在且可访问 for ($i = 0; $i -lt $maxRetries; $i++) { try { # 尝试打开文件以确认是否可访问 $fileStream = [System.IO.File]::Open($filePath, 'Open', 'Read', 'ReadWrite') $fileStream.Close() $fileStream.Dispose() break } catch { if ($i -eq $maxRetries - 1) { Write-Host "多次尝试后仍无法访问文件: $filePath,错误: $($_.Exception.Message)" return } Start-Sleep -Milliseconds $retryDelay } } # 构造目标路径 $destFilePath = Join-Path -Path $destinationDirectory -ChildPath (Split-Path -Path $filePath -Leaf) # 重试复制文件 for ($i = 0; $i -lt $maxRetries; $i++) { try { Copy-Item -Path $filePath -Destination $destFilePath -Force -ErrorAction Stop Write-Host "成功复制文件: $filePath -> $destFilePath" break } catch { if ($i -eq $maxRetries - 1) { Write-Host "复制失败,已达最大重试次数。文件: $filePath,错误: $($_.Exception.Message)" } Start-Sleep -Milliseconds $retryDelay } } } # 注册事件处理器 Register-ObjectEvent -InputObject $watcher -EventName 'Created' -Action $action | Out-Null Register-ObjectEvent -InputObject $watcher -EventName 'Changed' -Action $action | Out-Null # 开始监控 $watcher.EnableRaisingEvents = $true Write-Host "开始监控目录: $sourceDirectory,目标目录: $destinationDirectory" Write-Host "按Ctrl+C停止监控..." # 保持控制台运行 try { while ($true) { Start-Sleep -Seconds 1 } } finally { # 清理资源 $watcher.EnableRaisingEvents = $false $watcher.Dispose() Get-Job | Remove-Job -Force Write-Host "监控已停止,资源已清理" }
关键改进说明
- 预创建目标目录:避免因目标目录不存在导致复制失败。
- 文件可访问性检查:通过尝试打开文件确认是否可读取,替代简单的
Test-Path,更准确判断文件是否准备好。 - 多级重试机制:对文件访问和复制操作分别设置重试,应对文件锁定和写入未完成的情况。
- 资源清理:在脚本终止时自动清理
FileSystemWatcher和事件作业,避免资源泄漏。 - 更详细的日志输出:便于排查问题,了解复制状态。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

