FileSystemWatcher可以监视文件或文件夹的更改,因此在将新文件复制到文件夹或删除或更改文件时,可以立即通知您的PowerShell代码。
通常,您会找到用于同步监视的示例代码,如下所示:
# make sure you adjust this
$PathToMonitor = "C:\scripts\test"
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $true
Write-Host "Monitoring content of $PathToMonitor"
explorer $PathToMonitor
while ($true) {
$Change = $FileSystemWatcher.WaitForChanged('All', 1000)
if ($Change.TimedOut -eq $false)
{
# get information about the changes detected
Write-Host "Change detected:"
$Change | Out-Default
# uncomment this to see the issue
#Start-Sleep -Seconds 5
}
else
{
Write-Host "." -NoNewline
}
}
这个例子很好用。将文件添加到要监视的文件夹或进行更改时,将检测到更改类型。您可以轻松地获取该信息并调用操作。例如,在您的IT中,人们可以将带有说明的文件拖放到放置文件夹中,并且脚本可以响应这些文件。
但是,这种方法有一个缺点:检测到更改时,控制权将返回到脚本,以便它可以处理更改。如果在您的脚本不再等待事件时发生另一个文件更改,则它将丢失。您可以轻松地自己检查一下:
在检测到更改时执行的代码中添加冗长的语句,例如“ Start-Sleep -Seconds 5”,然后将多个更改应用于您的文件夹。如您所见,将检测到第一个更改,但是在PowerShell保持繁忙的五秒钟间隔内,所有其他更改事件都丢失了。
在下一个的提示中,我们将确保您的FileSystemWatcher不会跳过任何更改!
在上一个技巧中,我们介绍了FileSystemWatcher,并说明了当您的处理程序代码花费太长时间时,它如何丢失文件系统更改。
要正确使用FileSystemWatcher,应异步使用它并确保它使用队列。因此,即使您的脚本忙于处理文件系统更改,它也应继续记录新的文件系统更改,并在PowerShell完成对先前更改的处理后再进行处理。
# make sure you adjust this to point to the folder you want to monitor
$PathToMonitor = "C:\scripts\test"
explorer $PathToMonitor
$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
$FileSystemWatcher.Path = $PathToMonitor
$FileSystemWatcher.IncludeSubdirectories = $true
# make sure the watcher emits events
$FileSystemWatcher.EnableRaisingEvents = $true
# define the code that should execute when a file change is detected
$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 ""
Write-Host $text -ForegroundColor Green
# you can also execute code based on change type here
switch ($ChangeType)
{
'Changed' { "CHANGE" }
'Created' { "CREATED"}
'Deleted' { "DELETED"
# uncomment the below to mimick a time intensive handler
<#
Write-Host "Deletion Handler Start" -ForegroundColor Gray
Start-Sleep -Seconds 4
Write-Host "Deletion Handler End" -ForegroundColor Gray
#>
}
'Renamed' {
# this executes only when a file was renamed
$text = "File {0} was renamed to {1}" -f $OldName, $Name
Write-Host $text -ForegroundColor Yellow
}
default { Write-Host $_ -ForegroundColor Red -BackgroundColor White }
}
}
# add 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"
try
{
do
{
Wait-Event -Timeout 1
Write-Host "." -NoNewline
} while ($true)
}
finally
{
# this gets executed when user presses CTRL+C
# remove the event handlers
Unregister-Event -SourceIdentifier FSChange
Unregister-Event -SourceIdentifier FSCreate
Unregister-Event -SourceIdentifier FSDelete
Unregister-Event -SourceIdentifier FSRename
# remove background jobs
$handlers | Remove-Job
# remove filesystemwatcher
$FileSystemWatcher.EnableRaisingEvents = $false
$FileSystemWatcher.Dispose()
"Event Handler disabled."
}
运行此命令时,将监视$ PathToMonitor中指定的文件夹的更改,并且当更改发生时,将发出一条消息。当您按CTRL + C时,脚本结束,并且所有事件处理程序都将在finally块中自动清除。
PS F:\> F:\FileSystemWatcherTest2.ps1
Watching for changes to C:\scripts\test
....
C:\scripts\test\新建文本文档.txt was Created at 2020/10/31 7:39:18
.....
C:\scripts\test\新建文本文档.txt was Changed at 2020/10/31 7:39:23
.....
C:\scripts\test\新建文本文档.txt was Changed at 2020/10/31 7:39:28
.
C:\scripts\test\新建文本文档.txt was Deleted at 2020/10/31 7:39:29
..............
PS F:\>
更重要的是:此代码在内部使用队列,因此当短时间内发生许多更改时,一旦PowerShell不再繁忙,所有更改都将得到处理。您可以通过取消注释代码中删除事件的标记部分来对此进行测试。现在,无论何时删除文件,处理程序都会花费额外的4秒钟。
即使删除了大量文件,它们最终也会全部列出。如前所述,此处介绍的方法比基于WaitForChanged()的同步处理程序更加可靠。