为何向D:\Folder放入PDF时,脚本内Merge PDFs.bat会多次执行?需仅执行一次
解决FileSystemWatcher触发多次导致Bat重复执行的问题
嗨,这个问题我之前踩过一模一样的坑!你的PowerShell脚本里Merge PDFs.bat多次执行,核心原因是FileSystemWatcher的Created事件会被系统的多步文件操作触发多次,再加上脚本没有做去重处理,自然就重复执行了。
为什么会多次触发?
主要有这两个常见场景:
- 文件写入的分步操作:当你把PDF复制到目标目录时,系统不是一次性把文件写完的——通常会先创建一个空文件,然后逐步写入内容,最后可能还要修改文件的属性(比如修改时间)。每一步操作都可能触发
Created或者关联的事件,导致你的脚本多次捕获到“文件创建”的信号。 - 系统/第三方程序的额外操作:比如杀毒软件会自动扫描新文件,Windows资源管理器会生成PDF的缩略图,这些操作都会对新创建的文件进行读写访问,间接触发FileSystemWatcher的事件。
而且你脚本里的Start-Sleep -s 15只是延迟执行,但并没有判断“这个文件是不是已经处理过”,所以只要有事件触发,就会再跑一次bat。
怎么修改脚本解决问题?
我们需要给脚本加两个关键逻辑:判断文件是否完全写入磁盘 + 记录已处理的文件避免重复执行。下面是修改后的完整脚本:
# 定义全局哈希表,用来记录已经处理过的文件路径 $processedFiles = @{} $folder = 'D:\Folder' $filter = '*.PDF' $fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property @{ IncludeSubdirectories = $false NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite' } Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action { param($event) $fileName = $event.SourceEventArgs.Name $fullFilePath = Join-Path $event.SourceEventArgs.FullPath.DirectoryName $fileName # 1. 跳过已经处理过的文件 if ($script:processedFiles.ContainsKey($fullFilePath)) { Write-Host "跳过已处理文件: $fileName" return } # 2. 等待文件完全写入(判断文件是否被锁定) $isFileLocked = $true while ($isFileLocked) { try { # 尝试以读写方式打开文件,能打开说明文件已写完 $fileStream = [System.IO.File]::Open($fullFilePath, 'Open', 'ReadWrite', 'None') $fileStream.Close() $isFileLocked = $false } catch { # 文件还在被写入,等待500毫秒再重试 Start-Sleep -Milliseconds 500 } } # 3. 标记当前文件为已处理 $script:processedFiles[$fullFilePath] = $true # 可选:清理哈希表中已不存在的文件记录,避免内存占用过大 $script:processedFiles.Keys | Where-Object { -not (Test-Path $_) } | ForEach-Object { $script:processedFiles.Remove($_) } # 4. 执行合并脚本 Write-Host "处理文件: $fileName,执行合并bat" Invoke-Item 'C:\Merge PDF\Merge PDFs.bat' }
关键逻辑说明
- 全局哈希表
$processedFiles:因为Action块有自己独立的作用域,所以用script:前缀来访问全局的哈希表,记录已经处理过的文件路径,避免重复触发。 - 文件锁定判断:通过尝试以读写方式打开文件,来确认文件是否已经完全写入磁盘——如果文件还在复制/写入中,系统会锁定文件,这时候我们就等待一会儿再重试,确保bat处理的是完整的PDF文件。
- 清理旧记录:定期移除哈希表中已经不存在的文件路径,防止哈希表随着时间变得过大,占用不必要的内存。
这样修改后,不管FileSystemWatcher触发多少次事件,同一个PDF文件只会触发一次bat的执行啦!
内容的提问来源于stack exchange,提问作者John Rueda
相关产品推荐
相关产品推荐

