Created
October 21, 2025 18:39
-
-
Save steviecoaster/fc7b98a155b6f75476be850436a12b33 to your computer and use it in GitHub Desktop.
Implement a FileSystemWatcher in PowerShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function Wait-File { | |
| <# | |
| .SYNOPSIS | |
| Monitors a file system path for changes and executes a script block in response to specified events. | |
| .DESCRIPTION | |
| The Wait-File function creates a FileSystemWatcher to monitor a specified directory for file system changes. | |
| It registers event handlers for specified events (Created, Changed, Deleted, Renamed) and executes a | |
| custom action when those events occur. The function runs continuously until manually stopped (Ctrl+C). | |
| .PARAMETER Path | |
| Specifies the path to the directory to monitor. The path must exist. | |
| .PARAMETER Filter | |
| Specifies the type of files to watch. Use wildcards to watch multiple file types. | |
| Examples: "*.txt", "*.log", "*.*" | |
| .PARAMETER IncludeSubdirectories | |
| When specified, monitors subdirectories within the specified path. | |
| .PARAMETER AttributeFilter | |
| Specifies the type of changes to monitor. Valid values include: | |
| - FileName: Watches for file name changes | |
| - DirectoryName: Watches for directory name changes | |
| - Attributes: Watches for attribute changes | |
| - Size: Watches for file size changes | |
| - LastWrite: Watches for last write time changes | |
| - LastAccess: Watches for last access time changes | |
| - CreationTime: Watches for creation time changes | |
| - Security: Watches for security changes | |
| .PARAMETER RegisterEvent | |
| Specifies which file system events to monitor along with their corresponding actions. | |
| Each hashtable must contain: | |
| - EventName: The event type to monitor ('Changed', 'Created', 'Deleted', or 'Renamed') | |
| - Action: A script block to execute when the event occurs | |
| The script block receives the event arguments automatically through $Event. | |
| .EXAMPLE | |
| $events = @( | |
| @{ EventName = 'Changed'; Action = { Write-Host "File changed: $($Event.SourceEventArgs.Name)" } } | |
| ) | |
| Wait-File -Path "C:\Logs" -Filter "*.log" -AttributeFilter ([System.IO.NotifyFilters]::LastWrite) -RegisterEvent $events | |
| Monitors the C:\Logs directory for changes to .log files and displays a message when a file is modified. | |
| .EXAMPLE | |
| $events = @( | |
| @{ EventName = 'Created'; Action = { Write-Host "[CREATED] $($Event.SourceEventArgs.Name) at $(Get-Date)" } } | |
| @{ EventName = 'Deleted'; Action = { Write-Host "[DELETED] $($Event.SourceEventArgs.Name) at $(Get-Date)" } } | |
| ) | |
| Wait-File -Path "C:\Data" -Filter "*.*" -IncludeSubdirectories -AttributeFilter ([System.IO.NotifyFilters]::FileName) -RegisterEvent $events | |
| Monitors C:\Data and all subdirectories for file creation and deletion events with different actions for each event type. | |
| .NOTES | |
| - Press Ctrl+C to stop monitoring | |
| - The function runs indefinitely until interrupted | |
| - Multiple events can be registered simultaneously | |
| - Event handlers are automatically cleaned up when the function exits | |
| .LINK | |
| Register-ObjectEvent | |
| System.IO.FileSystemWatcher | |
| #> | |
| [CmdletBinding()] | |
| Param( | |
| [Parameter(Mandatory)] | |
| [ValidateScript({ Test-Path $_ })] | |
| [String] | |
| $Path, | |
| [Parameter(Mandatory)] | |
| [String] | |
| $Filter, | |
| [Parameter()] | |
| [Switch] | |
| $IncludeSubdirectories, | |
| [Parameter(Mandatory)] | |
| [System.IO.NotifyFilters[]] | |
| $AttributeFilter, | |
| [Parameter(Mandatory)] | |
| [ValidateScript({ | |
| foreach ($item in $_) { | |
| if ($item -isnot [Hashtable]) { | |
| throw "Each item in RegisterEvent must be a hashtable" | |
| } | |
| if (-not $item.ContainsKey('EventName')) { | |
| throw "Each hashtable must contain an 'EventName' key" | |
| } | |
| if ($item.EventName -notin @('Changed','Created','Deleted','Renamed')) { | |
| throw "EventName must be one of: Changed, Created, Deleted, Renamed" | |
| } | |
| if (-not $item.ContainsKey('Action')) { | |
| throw "Each hashtable must contain an 'Action' key" | |
| } | |
| if ($item.Action -isnot [ScriptBlock]) { | |
| throw "Action must be a ScriptBlock" | |
| } | |
| } | |
| return $true | |
| })] | |
| [Hashtable[]] | |
| $RegisterEvent | |
| ) | |
| end { | |
| try { | |
| $fsw = [System.IO.FileSystemWatcher]::new($Path, $Filter) | |
| $fsw.IncludeSubdirectories = $IncludeSubdirectories | |
| $fsw.NotifyFilter = $AttributeFilter | |
| $handlers = [System.Collections.Generic.List[psobject]]::New() | |
| foreach($re in $RegisterEvent){ | |
| $handler = Register-ObjectEvent -InputObject $fsw -EventName $re.EventName -Action $re.Action | |
| $handlers.Add($handler) | |
| } | |
| $fsw.EnableRaisingEvents = $true | |
| do { Wait-Event -Timeout 1 } | |
| while ($true) | |
| } | |
| finally { | |
| $fsw.EnableRaisingEvents = $false | |
| $handlers | Foreach-Object { Unregister-Event -SourceIdentifier $_.Name } | |
| $handlers | Remove-Job | |
| $fsw.Dispose() | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment