Powershell FileSystemWatcher - 避免在创建时重复操作

Posted

技术标签:

【中文标题】Powershell FileSystemWatcher - 避免在创建时重复操作【英文标题】:Powershell FileSystemWatcher - Avoiding duplicate action on Create 【发布时间】:2019-10-08 08:39:20 【问题描述】:

FileSystemWatcher 在事件中触发两次是 Powershell 中的一个已知问题。我正在尝试解决此问题,因为我正在查看正在创建的文件,然后将其推送到打印机。双重触发意味着我得到了重复的打印输出

我知道以前有人问过这个问题,但是当谈到 Powershell(实际上是一般的脚本)时,我是一个完全的新手,所以一些答案已经直接浮现在我的脑海中

在代码中,我正在查看一个文件夹,然后将子目录名称作为打印机名称传递给发送作业。这是因为正在使用的软件正在将 pdf 文件从远程位置复制到这些文件夹中(由于 citrix,该软件无法直接访问打印机)

### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = "L:\Label\"
    $watcher.Filter = "*.*"
    $watcher.IncludeSubdirectories = $true
    $watcher.EnableRaisingEvents = $true 

### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
    $action =  $path = $Event.SourceEventArgs.FullPath
                $changeType = $Event.SourceEventArgs.ChangeType
                $printer = Split-Path (Split-Path $path -Parent) -Leaf
                $logline = "$(Get-Date), $changeType, $path, $printer"
                Add-content "c:\prog\log.txt" -value $logline
                C:\prog\SumatraPDF.exe -print-to "\\http://srv:631\$printer" $path
                  

### DECIDE WHICH EVENTS SHOULD BE WATCHED 
    Register-ObjectEvent $watcher "Created" -Action $action
    while ($true) sleep 5

我希望看到打印命令(Sumatra 调用)仅在将 pdf 文件放入监视文件夹时出现一次

【问题讨论】:

【参考方案1】:

我认为 FileSystemWatcher 在事件上触发两次不是已知问题,而不是您从哪里获得该信息。

不管怎样,如果我是你,我不会自己在 Powershell 中编写 FileSystemWather 事件,这真的很痛苦。

相反,您可以使用 Powershell Guard,只需传递打印命令而不是 TestCommand。 https://github.com/smurawski/PowerShellGuard

PowerGuard 所做的只是抽象 FileSystemWatcher 的使用。您可以在 Powershell 脚本中创建打印命令,然后让 PowerGuard 使用 -TestCommand "Print.ps1 .\PathToYourFile" 调用您的脚本

最终解决方案(由发帖人本人):

dir \\srv\label\prnlblCuts\*.pdf | New-Guard -TestCommand "C:\PROG\SumatraPDF.exe -print-to \\srv-tsv:631\prnlblCuts" -TestPath "$($_.FullName)" -Wait

【讨论】:

我只说“已知问题”,因为快速谷歌发现很多人抱怨事件的多次触发。我敢肯定这是设计使然,但处理起来真的很痛苦:) 我现在正在玩 PowershellGuard。不过我的困惑是我是否仍然可以像在原始 Powershell 脚本中那样传递变量? 我明白你在说什么,但我认为这个问题被广泛歪曲了。很多人都在使用 FileSystemWatcher 并理解它触发的事件。如果您需要使用 PowerGuard 模块的示例,请告诉我。 啊!好的,我会试一试:) 我看不到使用 Guard 映射的网络驱动器。如果我执行 -Path c:\ -PathFilter "*.pdf",则会触发脚本但是我需要查看 l:\(映射的网络驱动器)。不过似乎不允许我这样做【参考方案2】:

这里没有告诉你应该做什么,不应该做什么,而是按照你的要求去做:

### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$global:canDoEvent = $True #NEW
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "L:\Label\"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true 

在检测到事件后定义操作

$action =  if ($global:canDoEvent)  #NEW
                $global:canDoEvent = $False #NEW
                $path = $Event.SourceEventArgs.FullPath
                $changeType = $Event.SourceEventArgs.ChangeType
                $printer = Split-Path (Split-Path $path -Parent) -Leaf
                $logline = "$(Get-Date), $changeType, $path, $printer"
                Add-content "c:\prog\log.txt" -value $logline
                C:\prog\SumatraPDF.exe -print-to "\\http://srv:631\$printer" $path
            
              

决定应该观看哪些事件

Register-ObjectEvent $watcher "Created" -EventName newFile -Action $action #NEW
do  #NEW
     $global:canDoEvent = $True
     Wait-Event -SourceIdentifier newFile -Timeout 1
    while ($True)
     

这可能需要调整,我不是专家,但就是这样。

基本上,添加一个全局布尔值 var = True,将您的等待事件置于 do-while 循环内的超时状态,在每个循环中将变量设为 true,然后在您的事件操作中将其设为 false。您的超时将定义事件可以触发的频率。一秒钟应该足以防止多次触发事件。显然,如果在某些情况下可以在同一秒内创建和打印超过 1 个唯一文件,它将跳过它们。

【讨论】:

以上是关于Powershell FileSystemWatcher - 避免在创建时重复操作的主要内容,如果未能解决你的问题,请参考以下文章

powershell PowerShell:启动PowerShell作为其他用户提升

powershell [提示输入powershell] #powershell #input #prompt

powershell 测量PowerShell命令或PowerShell脚本的速度

powershell远程下载exe并执行

powershell 使用啥端口

powershell PowerShell使用PowerShell创建本地管理员帐户