Powershell由于Filesystemwatcher而挂起

时间:2019-06-05 00:48:38

标签: powershell buffer filesystemwatcher

对于某些打印问题(由citrix和远程服务器引起),我们有一个复杂的解决方案。基本上是从主服务器上,我们强制将pdf文件推送到远程计算机,然后有一个Powershell脚本,该脚本不断在远程计算机上运行以“捕获”文件并将其推送到本地打印机

这可以“很好”

但是,我们会随机退出。 powershell脚本似乎没有崩溃,因为它仍在Windows中运行,但实际操作似乎并未在处理新文件

我今天已经做了大量的阅读,并且提到必须在完成后命名和注销事件,否则会导致缓冲区溢出问题并使powershell停止处理该动作。但是我不确定它应该在代码中的实际位置。想法是该脚本将永久运行,那么我们是否要在动作本身或其他地方取消注册或删除事件?

我以前在动作中进行了大量虚拟登录,以尝试查找失败的地方,但是它似乎在没有任何正当理由的情况下停在了不同的位置(即,在查找文件或其他命令时失败)命令移动等的时间

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

### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = { $path = $Event.SourceEventArgs.FullPath
            $changeType = $Event.SourceEventArgs.ChangeType
            $scandir="l:\files\cut" 
            $scanbackdir="l:\files\cut\back"   
            $scanlogdir="l:\files\cut\log" 
            $sumatra="l:\SumatraPDF.exe"      
            $pdftoprint=""
            $printername= "MainLBL"

### Get the List of files in the Directory, print file, wait and then move file
Get-ChildItem -Path $scandir -filter "*.pdf" -Name | % { 
$pdftoprint=$_ 
& $sumatra -silent $scandir\$pdftoprint -print-to $printername

sleep 3 

Move-Item -force $scandir\$pdftoprint $scanbackdir
 }
 } 

### Define what happens when script fails
$erroraction = {echo $(get-date) the process crashed | Out-File -Append l:\files\cut\log\errorlog.txt}    

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

2 个答案:

答案 0 :(得分:1)

如果您希望脚本在后台运行,请查看PowerShell后台作业。

如果您希望脚本永久运行,那么您希望使其成为服务...

请参阅以下内容:

How to Create a User-Defined Service

How to run a PowerShell script as a Windows service

...或将其附加到计划的任务,将在重新启动后重新启动。

有两种方法可以实现FileSystemWatcher。

  • 同步
  • 异步

从本质上讲,同步FileSystemWatcher会在检测到更改时将控制权返回给脚本,以便它可以处理更改。如果在您的脚本不再等待事件时发生另一个文件更改,则它将丢失。因此会导致意外的结果。

异步使用FileSystemWatcher,它将继续记录新的文件系统更改,并在PowerShell完成对先前更改的处理后对其进行处理。

*一个示例-示例异步FileSystemWatcher *

### New-FileSystemWatcherAsynchronous

# Set the folder target
$PathToMonitor = Read-Host -Prompt 'Enter a folder path'

$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path  = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $true

# Set emits events
$FileSystemWatcher.EnableRaisingEvents = $true

# Define change actions
$Action = {
    $details = $event.SourceEventArgs
    $Name = $details.Name
    $FullPath = $details.FullPath
    $OldFullPath = $details.OldFullPath
    $OldName = $details.OldName
    $ChangeType = $details.ChangeType
    $Timestamp = $event.TimeGenerated
    $text = "{0} was {1} at {2}" -f $FullPath, $ChangeType, $Timestamp

    Write-Host $text -ForegroundColor Green

    # Define change types
    switch ($ChangeType)
    {
        'Changed' { "CHANGE" }
        'Created' { "CREATED"}
        'Deleted' { "DELETED"
                    # Set time intensive handler
                    Write-Host "Deletion Started" -ForegroundColor Gray
                    Start-Sleep -Seconds 3    
                    Write-Warning -Message 'Deletion complete'
                  }
        'Renamed' { 
                    $text = "File {0} was renamed to {1}" -f $OldName, $Name
                    Write-Host $text -ForegroundColor Yellow
                  }
        default { Write-Host $_ -ForegroundColor Red -BackgroundColor White }
    }
}

# Set event handlers
$handlers = . {
    Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Changed -Action $Action -SourceIdentifier FSChange
    Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action $Action -SourceIdentifier FSCreate
    Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Deleted -Action $Action -SourceIdentifier FSDelete
    Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Renamed -Action $Action -SourceIdentifier FSRename
}

Write-Host "Watching for changes to $PathToMonitor" -ForegroundColor Cyan

try
{
    do
    {
        Wait-Event -Timeout 1
        Write-Host '.' -NoNewline

    } while ($true)
}
finally
{
    # End script actions + CTRL+C executes the remove event handlers
    Unregister-Event -SourceIdentifier FSChange
    Unregister-Event -SourceIdentifier FSCreate
    Unregister-Event -SourceIdentifier FSDelete
    Unregister-Event -SourceIdentifier FSRename

    # Remaining cleanup
    $handlers | 
    Remove-Job

    $FileSystemWatcher.EnableRaisingEvents = $false
    $FileSystemWatcher.Dispose()

    Write-Warning -Message 'Event Handler completed and disabled.'
}

答案 1 :(得分:0)

我还没有遇到可以在Windows上永久运行的脚本。 因此,考虑到这一点,我们认为理所当然会发生一些您无法控制的问题,例如network或电源或系统关闭。 考虑到这一点,我们为该脚本提供了一个生命周期,最后应适当清理所有内容。在这种情况下,我们有while循环,理论上应该永远不会结束,但是如果抛出异常,它将结束。在while循环中,如果任何事件都已注销,我们可以重新注册它们。如果观察者已被处置,我们可以重新创建它和事件。如果这确实是关键任务代码,那么我将把.net看作是hangfirenlog作为Windows服务的替代方案。

### WRAP Everything in a try finally so we dispose of events 
try {
    ### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
    $watcherArgs = @{
        Path = "l:\files\cut"
        Filter = "*.pdf"
        IncludeSubdirectories = $false
        EnableRaisingEvents = $true  
    }
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = $watcherArgs.Path
    $watcher.Filter = $watcherArgs.Filter
    $watcher.IncludeSubdirectories = $watcherArgs.IncludeSubdirectories
    $watcher.EnableRaisingEvents = $watcherArgs.EnableRaisingEvents

    ### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
    $action = { $path = $Event.SourceEventArgs.FullPath
                $changeType = $Event.SourceEventArgs.ChangeType
                $scandir="l:\files\cut" 
                $scanbackdir="l:\files\cut\back"   
                $scanlogdir="l:\files\cut\log" 
                $sumatra="l:\SumatraPDF.exe"      
                $pdftoprint=""
                $printername= "MainLBL"

    ### Get the List of files in the Directory, print file, wait and then move file
    Get-ChildItem -Path $scandir -filter "*.pdf" -Name | % { 
            $pdftoprint=$_ 
            if($LASTEXITCODE -ne 0) { 
                # Do something
                # Reset so we know when sumatra fails
                $LASTEXITCODE = 0
            }
            & $sumatra -silent $scandir\$pdftoprint -print-to $printername
            if($LASTEXITCODE -ne 0) { 
                # Do something to handle sumatra
            }
            sleep 3 

            # Split up copy and delete so we never loose files
            [system.io.file]::Copy("$scandir\$pdftoprint", "$scanbackdir", $true)
            [system.io.file]::Delete("$scandir\$pdftoprint")
        }
    } 

    ### Define what happens when script fails
    $erroraction = { 
        echo "$(get-date) the process crashed" | Out-File -Append "l:\files\cut\log\errorlog.txt"
    }    

    ### DECIDE WHICH EVENTS SHOULD BE WATCHED 
    $ErrorEvent  = Register-ObjectEvent $watcher "Error" -Action $erroraction
    $CreatedEvent = Register-ObjectEvent $watcher "Created" -Action $action
    $ListOfEvents = @(
        $ErrorEvent  
        $CreatedEvent
    )

    while ($true) {

        $eventMissing = $false
        $ListOfEvents | % { 
            $e = $_

            if (!(Get-Event -SourceIdentifier $e.Name -ErrorAction SilentlyContinue)) {
                # Event does not exist 
                $eventMissing = $true
            }
        }

        if(!$watcher || $eventMissing -eq $true) {
            # deregister events 
            $ListOfEvents | % { 
                $e = $_
                try {
                    Unregister-Event -SourceIdentifier $e.Name
                } catch { 
                    # Do Nothing
                }
            }
            if($watcher) {
                $watcher.Dispose()
                $watcher = $null   
            } else {
                # Create watcher 
                $watcher = New-Object System.IO.FileSystemWatcher
                $watcher.Path = $watcherArgs.Path
                $watcher.Filter = $watcherArgs.Filter
                $watcher.IncludeSubdirectories = $watcherArgs.IncludeSubdirectories
                $watcher.EnableRaisingEvents = $watcherArgs.EnableRaisingEvents
                $ErrorEvent  = Register-ObjectEvent $watcher "Error" -Action $erroraction
                $CreatedEvent = Register-ObjectEvent $watcher "Created" -Action $action
                $ListOfEvents = @(
                    $ErrorEvent  
                    $CreatedEvent
                )
            }

        }

        if ($watcher.EnableRaisingEvents -eq $false) {
            $watcher.EnableRaisingEvents = $watcherArgs.EnableRaisingEvents
        }

        sleep 5
    }

} finally {
    $ListOfEvents | % { 
        $e = $_
        try {
            Unregister-Event -SourceIdentifier $e.Name
        } catch { 
            # Do Nothing
        }
    }
    if($watcher) {
        $watcher.Dispose();
    }
}