PS> Use the FileSystemWatcher class to monitor a folder instead of looping.
Background
At some point or another, an administrator will ask themselves, "I just need some way to monitor changes to a specific folder, then move/copy/delete/open/print that file when it shows up." After a little bit of digging, more often than not, said administrator will build some kind of script that will check the status of a file/folder every few seconds.An alternative to that approach would be to use the FileSystemWatcher .NET class. The FileSystemWatcher class listens to the filesystem change notifications and raises events when a directory, or file in a directory, changes. It is provided by the System.IO namespace.
Benefits of the FileSystemWatcher class
Benefits:- Use fewer resources -- it no longer needs a constant Do-While or For loops.
- No more "sleep" duration settings to constantly tweak.
- Fewer compatibility risks because it's been available since .NET 1.0.
- Fewer tweaks = easier scale.
- If anything, it's still a fun learning experience to the uninitiated.
Cons:
- A little bit more complex to troubleshoot.
- Fewer tweaks = easier scale.
- If anything, it's still a fun learning experience to the uninitiated.
Cons:
- A little bit more complex to troubleshoot.
Script
The following script is a basic one that watches a network shared folder for the creation of any Word template files (*.dot) and moves them to a subfolder.# -----[Declarations]----- $FSWatcher= New-Object System.IO.FileSystemWatcher $FSWatcher.IncludeSubdirectories = $false $FSWatcher.Path = "C:\HoldMyBeer\WatchThis" # Alternate example "\\SERVER1\HIDDENSHARE$\$env:Username\Documents\WatchThisFolder" $FSWatcher.Filter = "*.dot" $FSWatcher.NotifyFilter = [System.IO.NotifyFilters]'FileName, LastWrite' $FSWatcher.EnableRaisingEvents = $true # DEBUG # $FSWatcher | Get-Member -MemberType Properties,Event $ScanDir = $FSWatcher.Path $ScanLogDir = "\\\ \$env:Username\FolderToWatch\" # Optional: Create a Log file $WatchLog = Join-Path -Path $ScanLogDir ` -ChildPath "$($MyInvocation.MyCommand.Name)-$(Get-Date -Format 'MM-dd-yyyy').log" # Define a ScriptBlock action $PrintPdf = { $PdfToPrint = $event.SourceEventArgs.FullPath $ChangeType = $event.SourceEventArgs.ChangeType Write-Host "Locked $PdfToPrint was $ChangeType at $(Get-Date)" While ($True) { Try { [IO.File]::OpenWrite($PdfToPrint).Close() Break } Catch { Start-Sleep -Milliseconds 200} } Write-Host "Available $PdfToPrint was $ChangeType at $(Get-Date)" # -----[Print the PDF file]----- # Check in the Sumatra GUI that it uses the same printer name as in the 'Select Printer' section $printername = ((Get-WmiObject -Class Win32_Printer).where({$PSItem.Default -eq $true})).Name # Cleanup old Log files - Change AddDays value as required Get-ChildItem "$scanlogdir\*.log" | Where LastWriteTime -LT (Get-Date).AddDays(-15) | Remove-Item -Confirm:$false # Log event and action "$(get-date) - Printing file - $pdftoprint on Printer - $printername" | Out-File -Append $scanlogname # Print Start-Process -Wait -FilePath $sumatra -ArgumentList "$pdftoprint -print-to ""$printername""" Start-Sleep -Seconds 5 # Increase Sleep if slow to print otherwise PDF will be removed before it has time to print # Move or Remove PDF - Will overwrite if same name exists in the backup directory "$(get-date) - Moving file - $pdftoprint to - $scanbackdir directory" | Out-File -Append $scanlogname Remove-Item -force $pdftoprint | Out-File -Append $scanlogname } # Register the Event to listen for Register-ObjectEvent $FSWatcher 'Created' -Action $PrintPdf # Reference only: Manually unregister the FileSystemWatcher event # Get-EventSubscriber | Unregister-Event
How it works
In a nutshell, the script will register script-block ($PrintPDF) to the events generated by the .NET object, which, in this case, is the the FileSystemWatcher object ($FSWatcher).The actions ($FSWatcher.NotifyFilter) define what actions will raise an event that will trigger the execution of the script-block.
Optionally, you can run the Get-EventSubscriber to list the registrations within your current scope, and the Unregister-Event to kill those registrations as you are debugging your versions of the script.
How to run it
There are several ways to initiate the script. It's important that, by design, the subscription to the FileSystemWatcher events are destroyed when the Powershell script is terminated. This way, subscriptions do not accumulate on a given machine, which may lead toCommand:
powershell.exe -noexit -noprofile -windowstyle hidden -file "(folder)\Move-DotFiles.ps1"
As always, please note the ExecutionPolicy before running the command.
No comments