Skip to content

Latest commit

 

History

History
270 lines (210 loc) · 10.8 KB

Tips - How to monitor files on a folder and do something when something appears.md

File metadata and controls

270 lines (210 loc) · 10.8 KB

How to monitor files on a folder and do something when something appears

Préambule

The use of .NET classes can extend Posh capabilities.

In the present user case, I will use the System.IO.FileSystemWatcher .NET Class.

2 ways to create the watcher

$Watcher = [System.IO.FileSystemWatcher]::new()

Or another classic way

$Watcher = New-Object System.IO.FileSystemWatcher

Deep dive on the $Watcher

Now, let's examine the $Watcher variable and properties

$Watcher | get-member
   TypeName : System.IO.FileSystemWatcher

Name                      MemberType Definition
----                      ---------- ----------
Changed                   Event      System.IO.FileSystemEventHandler Changed(System.Object, System.IO.FileSystemEventArgs)
Created                   Event      System.IO.FileSystemEventHandler Created(System.Object, System.IO.FileSystemEventArgs)
Deleted                   Event      System.IO.FileSystemEventHandler Deleted(System.Object, System.IO.FileSystemEventArgs)
Disposed                  Event      System.EventHandler Disposed(System.Object, System.EventArgs)
Error                     Event      System.IO.ErrorEventHandler Error(System.Object, System.IO.ErrorEventArgs)
Renamed                   Event      System.IO.RenamedEventHandler Renamed(System.Object, System.IO.RenamedEventArgs)
BeginInit                 Method     void BeginInit(), void ISupportInitialize.BeginInit()
CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Dispose                   Method     void Dispose(), void IDisposable.Dispose()
EndInit                   Method     void EndInit(), void ISupportInitialize.EndInit()
Equals                    Method     bool Equals(System.Object obj)
GetHashCode               Method     int GetHashCode()
GetLifetimeService        Method     System.Object GetLifetimeService()
GetType                   Method     type GetType()
InitializeLifetimeService Method     System.Object InitializeLifetimeService()
ToString                  Method     string ToString()
WaitForChanged            Method     System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType), System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes cha...
Container                 Property   System.ComponentModel.IContainer Container {get;}
EnableRaisingEvents       Property   bool EnableRaisingEvents {get;set;}
Filter                    Property   string Filter {get;set;}
IncludeSubdirectories     Property   bool IncludeSubdirectories {get;set;}
InternalBufferSize        Property   int InternalBufferSize {get;set;}
NotifyFilter              Property   System.IO.NotifyFilters NotifyFilter {get;set;}
Path                      Property   string Path {get;set;}
Site                      Property   System.ComponentModel.ISite Site {get;set;}
SynchronizingObject       Property   System.ComponentModel.ISynchronizeInvoke SynchronizingObject {get;set;}

As we can see, the Property NotifyFilter is a System.IO.NotifyFilters .NET class

Deep dive in this class

[System.IO.NotifyFilters]

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     NotifyFilters                            System.Enum

This is a System.Enum BaseType

[System.IO.NotifyFilters].BaseType

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Enum                                     System.ValueType

and by using the Enum type, we can view the values of System.IO.NotifyFilters using the GetNames Method

[Enum]::GetNames('System.IO.NotifyFilters')
FileName
DirectoryName
Attributes
Size
LastWrite
LastAccess
CreationTime
Security

Then we can use one, or more of these values to define our NotifyFilter property

The other properties are simple to understand by themselves.

Now let's code this

1 : define the watcher

# Path to Input Folder
$InputFolder = "\\path\to\folder"
# filter to apply according to your requirements
$filter = '*.*'
$Watcher.Path                  = $InputFolder   # Input Folder to watch
$Watcher.Filter                = $Filter        # Filter to apply (i.e. "*.", "*.jpg")
$Watcher.IncludeSubdirectories = $true          # Define if Watching only root folder or subfolders too
$Watcher.EnableRaisingEvents   = $true          # Boolean value to indicate enablement of the watcher. To be able to subscribe to the events generated by .NET Framework objects
$Watcher.NotifyFilter          = [IO.NotifyFilters]'FileName, LastWrite'  # Define NotifyFilter to apply.

We can review and Display Watcher properties

$Watcher

NotifyFilter          : FileName, LastWrite
EnableRaisingEvents   : True
Filter                : *.*
IncludeSubdirectories : True
InternalBufferSize    : 8192
Path                  :
Site                  :
SynchronizingObject   :
Container             :

2 : Define the actions of the Watcher after a event is detected

$Action = {
           $Path = $Event.SourceEventArgs.FullPath
           $Name = $Event.SourceEventArgs.Name
           $ChangeType = $Event.SourceEventArgs.ChangeType
           $TimeStamp = $Event.TimeGenerated
           # Add all actions to do. See the following example
           # Move-Item $Path -Destination $Backup -Force -Verbose  # Force will overwrite files with same name
           #$LogLine = "$(Get-Date), $ChangeType, $Path"           # Define content for log
           # Add-content -Path $LogFile -value $LogLine            # add to log file (already existing)
           }

3 - Decide which events should be watched + Set check frequency

$OnCreated = Register-ObjectEvent -InputObject $Watcher -EventName Created -SourceIdentifier FileCreated -Action $Action

At this step all events are monitored by the watcher. To stop the watcher, use

Unregister-Event -SourceIdentifier FileCreated

4 - Test all code with a sample

$Watcher = [System.IO.FileSystemWatcher]::new()
# Path to Input Folder
$InputFolder = "C:\temp"
# Path to Backup Folder
$Backup = "C:\temp2"
# Path Log File
$LogFile = "c:\temp3\log.txt"
# filter to apply
$filter = '*.*'
$Watcher.Path                  = $InputFolder   # Input Folder to watch
$Watcher.Filter                = $Filter        # Filter to apply (i.e. "*.", "*.jpg")
$Watcher.IncludeSubdirectories = $true          # Define if Watching only root folder or subfolders too
$Watcher.EnableRaisingEvents   = $true          # Boolean value to indicate enablement of the watcher. To be able to subscribe to the events generated by .NET Framework objects
$Watcher.NotifyFilter          = [IO.NotifyFilters]'FileName, LastWrite'  # Define NotifyFilter to apply.
$Action = {
           $Path = $Event.SourceEventArgs.FullPath
           $Name = $Event.SourceEventArgs.Name
           $ChangeType = $Event.SourceEventArgs.ChangeType
           $TimeStamp = $Event.TimeGenerated
           Move-Item $Path -Destination $Backup -Force -Verbose  # Force will overwrite files with same name
           $LogLine = "$(Get-Date), $ChangeType, $Path"          # Define content for log
           Add-content -Path $LogFile -value $LogLine            # add to log file (already existing)
           Write-Output " Moving action added to $LogFile"
           }
$OnCreated = Register-ObjectEvent -InputObject $Watcher -EventName Created -SourceIdentifier FileCreated -Action $Action

New-Item -Path c:\temp\NewtestFile.txt

    Répertoire : C:\temp


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       12/02/2020     13:53              0 NewtestFile.txt

COMMENTAIRES : Opération « Déplacer le fichier » en cours sur la cible « Élément : C:\temp\NewtestFile.txt Destination : C:\temp2\NewtestFile.txt ».

Get-Content C:\Temp3\Log.txt
02/12/2020 13:52:14, Created, C:\temp\Nouveau Microsoft Excel Worksheet.xlsx
02/12/2020 13:52:14, Created, C:\temp\~ouveau Microsoft Excel Worksheet.tmp
02/12/2020 13:52:15, Created, C:\temp\Nouveau Microsoft Excel Worksheet.xlsx~RF3bf2bda.TMP
02/12/2020 13:53:54, Created, C:\temp\NewtestFile.txt

Unregister-Event -SourceIdentifier FileCreated

Use a dedicated Posh module called PSWatch

The module unfortunately is not available in the PowerShell Gallery, but the creator's GitHub page does provide an installation PowerShell script that will create the module on a local machine.

You can see the Github site at this link : https://github.com/jfromaniello/pswatch, download or install it, or use the Install.ps1 file on the site to install or import the Module.

[Nota] The module contains only one function call watch with Only few parameters : [String]$location = "", [switch]$includeSubdirectories = $true, [switch]$includeChanged = $true, [switch]$includeRenamed = $true, [switch]$includeCreated = $true, [switch]$includeDeleted = $false

Let's use it with a sample

Import-module -Name PSWatch # previously think to unblock files if you have manually download Module files
watch -location c:\temp -includeSubdirectories -includeRenamed | foreach { Write-Output "Change made on $($_.Path)"}
Change made on c:\temp\ScheduledTasks.log

# You can also use simultaneously all parameters
watch -location c:\temp -includeSubdirectories -includeRenamed -includeChanged -includeCreated -includeDeleted | foreach { Write-Output "Change made on $($_.Path)"}
Change made on c:\temp\ScheduledTasks.log

[Nota 2] At this step the watcher is still alive

As a function, this could be use in a .ps1 script file.

Other use from the Github site :

Import-Module pswatch
watch "Myfolder\Other" |
    Foreach-Object {
                    Write-Host "$_.Path has changed!"
                    RunUnitTests.exe $_.Path
                   }

You can filter by using powershell pipelining as follows:

watch | Get-Item |
    Where-Object { $_.Extension -eq ".js" } |
    Foreach-Object {
                    do the magic...
                   }

Finally

This post was build based on informations found on the following

https://docs.microsoft.com/fr-fr/dotnet/api/system.io.filesystemwatcher?view=netframework-4.8 https://stackoverflow.com/questions/31795933/powershell-and-system-io-filesystemwatcher https://github.com/jfromaniello/pswatch https://4sysops.com/archives/monitor-file-changes-in-windows-with-powershell-and-pswatch/ https://4sysops.com/archives/the-system-io-filesystemwatcher-net-class-raise-file-system-notifications-in-powershell/ https://www.nextofwindows.com/how-to-monitor-a-folder-for-changes-the-easy-way

Hope this helpful