Skip to content

Instantly share code, notes, and snippets.

@steviecoaster
Created October 21, 2025 18:39
Show Gist options
  • Save steviecoaster/fc7b98a155b6f75476be850436a12b33 to your computer and use it in GitHub Desktop.
Save steviecoaster/fc7b98a155b6f75476be850436a12b33 to your computer and use it in GitHub Desktop.
Implement a FileSystemWatcher in PowerShell
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