Powershell Folder monitor/batch execution

5,487

FileWatchers have quirks, depending on how files are created:

You may notice in certain situations that a single creation event generates multiple Created events that are handled by your component. For example, if you use a FileSystemWatcher component to monitor the creation of new files in a directory, and then test it by using Notepad to create a file, you may see two Created events generated even though only a single file was created. This is because Notepad performs multiple file system actions during the writing process. Notepad writes to the disk in batches that create the content of the file and then the file attributes. Other applications may perform in the same manner. Because FileSystemWatcher monitors the operating system activities, all events that these applications fire will be picked up.

Note: Notepad may also cause other interesting event generations. For example, if you use the ChangeEventFilter to specify that you want to watch only for attribute changes, and then you write to a file in the directory you are watching using Notepad, you will raise an event . This is because Notepad updates the Archived attribute for the file during this operation.

So in your case I would go with plain directory comparing. Here is the script that will monitor directory for changes and run files. Save this script as MonitorAndExecute.ps1. It accepts following arguments:

  • Path: folder to monitor. If not specified, the current directory is used.
  • Filter: file extension to match. Default is *, i.e., match all files.
  • Run: file extension to run, when new file is found. Default is bat.
  • Recurse: recurse directories or not. Default is false.
  • Interval: time in seconds to sleep between folder scan. Default is 5 seconds.
  • Verbose: script will tell you what's going on via Write-Verbose messages.

Example (run from the PowerShell console).

Monitor *.pdf files in the folder D:\XXXX\XXXX, recurse, if the new file is found, run file with the same base name and extension *.bat, be verbose:

.\MonitorAndExecute.ps1 -Path 'D:\XXXX\XXXX' -Filter '*.pdf' -Run 'bat' -Recurse -Interval 10 -Verbose

MonitorAndExecute.ps1 script:

Param
(
    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [ValidateScript({
        if(!(Test-Path -LiteralPath $_ -PathType Container))
        {
            throw "Input folder doesn't exist: $_"
        }
        $true
    })]
    [ValidateNotNullOrEmpty()]
    [string]$Path = (Get-Location -PSProvider FileSystem).Path,

    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [string]$Filter = '*',

    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [string]$Run = 'bat',

    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [switch]$Recurse,

    [Parameter(ValueFromPipelineByPropertyName = $true)]
    [int]$Interval = 5
)

# Scriptblock that gets list of files
$GetFileSet = {Get-ChildItem -LiteralPath $Path -Filter $Filter -Recurse:$Recurse | Where-Object {!($_.PSIsContainer)}}

Write-Verbose 'Getting initial list of files'
$OldFileSet = @(. $GetFileSet)
do
{
    Write-Verbose 'Getting new list of files'
    $NewFileSet = @(. $GetFileSet)

    Write-Verbose 'Comaparing two lists using file name and creation date'
    Compare-Object -ReferenceObject $OldFileSet -DifferenceObject $NewFileSet -Property Name, CreationTime -PassThru |
        # Select only new files
        Where-Object { $_.SideIndicator -eq '=>' } |
            # For each new file...
            ForEach-Object {
                Write-Verbose "Processing new file: $($_.FullName)"
                # Generate name for file to run
                $FileToRun = (Join-Path -Path (Split-Path -LiteralPath $_.FullName) -ChildPath ($_.BaseName + ".$Run"))

                # If file to run exists
                if(Test-Path -LiteralPath $FileToRun -PathType Leaf)
                {
                    Write-Verbose "Running file: $FileToRun"
                    &$FileToRun
                }
                else
                {
                    Write-Verbose "File to run not found: $FileToRun"
                }
            }

    Write-Verbose 'Setting current list of files as old for the next loop'
    $OldFileSet = $NewFileSet

    Write-Verbose "Sleeping for $Interval seconds..."
    Start-Sleep -Seconds $Interval
}
while($true)
Share:
5,487

Related videos on Youtube

Richard W
Author by

Richard W

Updated on September 18, 2022

Comments

  • Richard W
    Richard W over 1 year

    I am new to Powershell. I have found a script that works with what I need, kind of.

    What I am looking for is a script to monitor a folder for specific file names/types. Depending on the file name/type, I want it to execute a certain batch file to execute a command for a server utility.

    How exactly would I achieve this.I found this script and added a invoke-item to launch a batfile one a file such as PDF lands in the folder. However, I need to filter it and have it launch a different bat file depending on the file name. What I currently have, it will invoke the bat file for each file that lands in the folder which I don't want. My knowedlge is minimal and I only know enough to be dangerous.

    If XXXX.PDF, XXRX.PDF, XXLX.PDF hit the folder, I need it to know if XXXX.PDF lands, run XXXX.bat only. If XXRX.PDF lands run XXRX.BAT only, etc, etc.

    ### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
        $watcher = New-Object System.IO.FileSystemWatcher
        $watcher.Path = "D:\XXXX\XXXX"
        $watcher.Filter = "*.PDF"
        $watcher.IncludeSubdirectories = $true
        $watcher.EnableRaisingEvents = $true 
    
    ### DEFINE ACTIONS AFTER A EVENT IS DETECTED
    
        $action = {Invoke-Item "D:\BATCH FILES\XXXXX.bat" -filter = "XXXXX.pdf"}    
    
    ### DECIDE WHICH EVENTS SHOULD BE WATCHED + SET CHECK FREQUENCY  
        $created = Register-ObjectEvent $watcher "Created" -Action $action
    ###    $changed = Register-ObjectEvent $watcher "Changed" -Action $action
    ###    $deleted = Register-ObjectEvent $watcher "Deleted" -Action $action
    ###    $renamed = Register-ObjectEvent $watcher "Renamed" -Action $action
        while ($true) {sleep 5}
    
    • Qwilson
      Qwilson about 9 years
      You should look at the help files for "Test-Path" and "If". Create your IF statement: if test-path = true, then perform some action (in this case you bat file).
  • Emil Borconi
    Emil Borconi over 8 years
    What a pity, spot on answer and it's neither accepted neither upvoted... shame... Thanks for the answer helped me out.