Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

check multiple files (Moved the Post from VBScriptto here) 1

Status
Not open for further replies.

sugram

Technical User
Feb 5, 2021
26
DE
Hello

i have postet here an question and there mikrom posted a powershell solution to me.
So I'm moving this to here.

I would like to check several files, ideally from several PCs, for changes and then receive a notification.
after some googling I have this script

Code:
Add-Type -AssemblyName PresentationFramework
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "c:\tmp\tk"
$watcher.Filter = "*.cfg"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
$null = Register-ObjectEvent $watcher "Changed" -Action {Send-MailMessage –To "Email.Adress@domain.com" –Subject "CFG DATEI GEÄNDERT!" –Body "CFG Datei wurde geändert!" –SmtpServer "smtp.com" –From "noreplay@domain.com" -encoding ([System.Text.Encoding]::UTF8)}
while ($true) {sleep 10}

it works, only I always get two email's.
But i don't understand why

Can you tell me why?
And how can I query multiple paths ?!
Because I would like to be able to query several PCs

And when i edit the script to open a Picture, i get multiple E-Mails
Code:
Add-Type -AssemblyName PresentationFramework
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "c:\tmp\tk"
$watcher.Filter = "*.cfg"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
$null = Register-ObjectEvent $watcher "Changed" -Action {Send-MailMessage –To "Email.Adress@domain.com" –Subject "CFG DATEI GEÄNDERT!" –Body "CFG Datei wurde geändert!" –SmtpServer "smtp.com" –From "noreplay@domain.com" -encoding ([System.Text.Encoding]::UTF8)}
$null = Register-ObjectEvent $watcher "Changed" -Action {Invoke-Item "C:\Users\username\Documents\test.jpg"}
while ($true) {sleep 10}

I hope that someone here can explain or help me, why I get several emails here, although only one file changes.
And of course how I could check several PCs

Many Thanks
 
>you should get only one message for every change

Nope. Very much depends on how an application updates a file. The workaround is to simply ignore duplicate events

> fault ... Notepad+
Yep! See above ...
 
mikrom said:
Oh then it wasn't Powershell's fault but Notepad++'s !
you wrote that you changed it with the notepad, I tried it with this one and it works.
Normally I always use notepadd ++ here


mikrom said:
Yes, it must be possible.

Many thanks for the Code!
I will test it.
 
Ok, give it a try and then please let us know how you managed to check different file paths.
 
Good Morning.

I noticed a new "error" with the Script yesterday.
I copy the Test.cfg File in a different folder, change the file and copy back to the watched folder.
So i get 3 Messages?!?!

I found a better solution for me.
(Here the URL With this Script i get always 1 Message.

So i have changed the script and actually it work's.
I hope it was correct

Code:
# find the path to the desktop folder:
#$desktop = [Environment]::GetFolderPath('Desktop')

# specify the path to the folder you want to monitor:
#$Path = $desktop
$Path = "c:\tmp"

# specify which files you want to monitor
$FileFilter = '*.cfg'  

# specify whether you want to monitor subfolders as well:
$IncludeSubfolders = $true

# specify the file or folder properties you want to monitor:
$AttributeFilter = [IO.NotifyFilters]::FileName, [IO.NotifyFilters]::LastWrite 

# specify the type of changes you want to monitor:
#$ChangeTypes = [System.IO.WatcherChangeTypes]::Created, [System.IO.WatcherChangeTypes]::Deleted , [System.IO.WatcherChangeTypes]::Changed
$ChangeTypes = [System.IO.WatcherChangeTypes]::Changed
# specify the maximum time (in milliseconds) you want to wait for changes:
$Timeout = 1000

# define a function that gets called for every change:
function Invoke-SomeAction
{
  param
  (
    [Parameter(Mandatory)]
    [System.IO.WaitForChangedResult]
    $ChangeInformation
  )
  
  Write-Warning 'Change detected:'
  $ChangeInformation | Out-String | Write-Host -ForegroundColor DarkYellow
  # send email
  Send-MailMessage –To "Email@domain.com" –Subject "CFG DATEI GEÄNDERT!" –Body "CFG Datei wurde geändert!" –SmtpServer "smtp.com" –From "noreplay@domain.com" -encoding ([System.Text.Encoding]::UTF8)
  Invoke-Item "C:\Users\username\Documents\test.jpg"


}

try
{
  Write-Warning "FileSystemWatcher is monitoring $Path"
    
  # create a filesystemwatcher object
  $watcher = New-Object -TypeName IO.FileSystemWatcher -ArgumentList $Path, $FileFilter -Property @{
    IncludeSubdirectories = $IncludeSubfolders
    NotifyFilter = $AttributeFilter
  }

  # start monitoring manually in a loop:
  do
  {
    # wait for changes for the specified timeout
    # IMPORTANT: while the watcher is active, PowerShell cannot be stopped
    # so it is recommended to use a timeout of 1000ms and repeat the
    # monitoring in a loop. This way, you have the chance to abort the
    # script every second.
    $result = $watcher.WaitForChanged($ChangeTypes, $Timeout)
    # if there was a timeout, continue monitoring:
    if ($result.TimedOut) { continue }
    
    Invoke-SomeAction -Change $result
    # the loop runs forever until you hit CTRL+C    
  } while ($true)
}
finally
{
  # release the watcher and free its memory:
  $watcher.Dispose()
  Write-Warning 'FileSystemWatcher removed.'
}



currently I want to implement your solution

Code:
$path = $Event.SourceEventArgs.FullPath
  $changeType = $Event.SourceEventArgs.ChangeType
  $logline = "$(Get-Date), $changeType, $path"
  # write to logfile
  Add-content "C:\tmp\FileWatcher_log.log" -value $logline
  # write to console
  Write-Host $logline
  # send email
  #Send-MailMessage –To "Email@domain.com" `
  #                 –Subject "Event on file: $path" `
  #                 –Body $logline `
  #                 –SmtpServer "smtp.com" `
  #                 –From "noreplay@domain.com" `
  #                 -encoding ([System.Text.Encoding]::UTF8)
  Invoke-Item "C:\Users\username\Documents\test.jpg"
}
 
hi again :)

I am currently failing to get the file name in my email notification just like in your script.
I hope you can help me :)

Many Thanks
 
That new script you posted is more complicated for me than the previous.

I tried only to replace Ctrl+C handling in the old script with try-finally (as shown in the new script) - it works, but then notification has stopped working ...
 
I adapted from the example called "Advanced Mode (asynchonous)", so that it writes output to console, to the logfile and it sends email. It's more similar to the older example I posted above and seems to work fine. It's advantage is that it handles Ctrl+C in the try-finally loop and so it does not have problems to run in PowerShell ISE.
 
Hello

I ask my question in the Forum and i get an answer.
With this script i get the Filename

Code:
Write-Warning 'Change detected:'
  $ChangeInformation | Out-String | Write-Host -ForegroundColor DarkYellow
  # send email
  Send-MailMessage –To "Email@domain.com" `
                   –Subject ("ACHTUNG CFG DATEI " + $ChangeInformation.Name ,"GEÄNDERT!")`
                   –Body ("CFG geändert " + $ChangeInformation.Name)`
                   –SmtpServer "smtp.com" `
                   –From "noreplay@domain.com" `
                   -encoding ([System.Text.Encoding]::UTF8)
 
I have one more question.
I have read here that the paths could also be read from a file

Code:
$paths = Get-Content "D:\abc\Folder_monitored_paths.txt"; 

foreach ($path in $paths)

is it possible to do that here as well.
My Powershell knowledge is absolutely insufficient for this
 
sugram said:
I have read here that the paths could also be read from a file
...
is it possible to do that here as well.

In the meantime I tried to build a loop directly into the script but it doesn't worked for me.
Then I got the idea to parametrize the example called "Advanced Mode (asynchonous)" from and to call it as background job.

So, first I parametrized the original script, changing in it these lines
Code:
...
...
# specify the path to the folder you want to monitor:
$Path = Split-Path -Path $args[0] -Parent

# specify which files you want to monitor
$FileFilter = Split-Path -Path $args[0] -Leaf
...
...
and named the modified script monitor_file.ps1.

Then, I wrote second script monitor_all_files.ps1 which calls the first script. It reads from the configuration file monitor_files.cfg full paths of all files to be monitored and for every file it calls the first script monitor_file.ps1 as background job. All started background jobs are marked in an ArrayList.
The monitoring could be stopped by pressing Ctrl+C: After that, the program iterates over the list of started background jobs and stops and removes every job.
It seems to work as I expected.

Here is the second script:
monitor_all_files.ps1
Code:
[COLOR=#0000ff]# read all file paths from configuration file[/color]
[COLOR=#008080]$paths[/color] = [COLOR=#804040][b]Get-Content[/b][/color] [COLOR=#ff00ff]"c:\tmp2\monitor_files.cfg"[/color];

[COLOR=#0000ff]# create ArrayList for job names[/color]
[COLOR=#008080]$my_jobs[/color]=[COLOR=#2e8b57][b][System.Collections.ArrayList][/b][/color]::new()
[COLOR=#008080]$nr[/color] = [COLOR=#ff00ff]0[/color]
[COLOR=#804040][b]foreach[/b][/color] ([COLOR=#008080]$path[/color] [COLOR=#804040][b]in[/b][/color] [COLOR=#008080]$paths[/color]) {
  [COLOR=#008080]$my_job[/color] = [COLOR=#804040][b]Start-Job[/b][/color] -FilePath .\monitor_file.ps1 -ArgumentList [COLOR=#008080]$path[/color]
  [COLOR=#0000ff]# append job name into array[/color]
  [COLOR=#008080]$nr[/color] = [COLOR=#008080]$my_jobs[/color].Add([COLOR=#008080]$my_job[/color].Name)
  [COLOR=#804040][b]Write-Host[/b][/color] [COLOR=#ff00ff]" .. starting job ""$($my_job.Name)"" for monitoring: ""$path"""[/color]
}

[COLOR=#0000ff]# Report all jobs[/color]
[COLOR=#804040][b]Write-Host[/b][/color]
[COLOR=#804040][b]Write-Host[/b][/color] [COLOR=#ff00ff]"Now running these jobs:"[/color]
[COLOR=#804040][b]Get-Job[/b][/color]
[COLOR=#804040][b]Write-Host[/b][/color]

[COLOR=#804040][b]Write-Warning[/b][/color] [COLOR=#ff00ff]"Press Ctrl+C to end monitoring !"[/color]
[COLOR=#804040][b]Write-Host[/b][/color]

try {
  [COLOR=#804040][b]while[/b][/color] ([COLOR=#008080]$true[/color]) {
    [COLOR=#804040][b]Wait-Event[/b][/color] -Timeout [COLOR=#ff00ff]1[/color]

    [COLOR=#0000ff]# write a dot to indicate we are still monitoring:[/color]
    [COLOR=#804040][b]Write-Host[/b][/color] [COLOR=#ff00ff]"."[/color] -NoNewline
  }
}
finally {
  [COLOR=#804040][b]Write-Host[/b][/color]
  [COLOR=#804040][b]Write-Warning[/b][/color] [COLOR=#ff00ff]"Now, all monitoring jobs will be stopped and then removed:"[/color]
  [COLOR=#804040][b]foreach[/b][/color] ([COLOR=#008080]$my_job[/color] [COLOR=#804040][b]in[/b][/color] [COLOR=#008080]$my_jobs[/color]) {
    [COLOR=#804040][b]Write-Host[/b][/color] [COLOR=#ff00ff]" .. stopping and removing $my_job"[/color] -NoNewline
    [COLOR=#804040][b]Stop-Job[/b][/color] -Name [COLOR=#008080]$my_job[/color]
    [COLOR=#804040][b]Remove-Job[/b][/color] -Name [COLOR=#008080]$my_job[/color]
    [COLOR=#804040][b]Write-Host[/b][/color] [COLOR=#ff00ff]" .. Done."[/color]
  }
  [COLOR=#804040][b]Write-Host[/b][/color]
  [COLOR=#804040][b]Write-Warning[/b][/color] [COLOR=#ff00ff]"Monitoring ends !"[/color]
}
 
[thumbsup2]
Many thanks.

In the first test, I had the problem that it started a powershell process for me in a continuous loop, so I had to reboot the PC :)
 
sugram said:
In the first test, I had the problem that it started a powershell process for me in a continuous loop, so I had to reboot the PC
:)
Don't worry, you are not the only one who had to experience such nasty problems with Powershell. I made a little mistake and it generated and sent me over 500 emails, which I then had to delete manually. And I also rebooted the PC several times in order to have to kill the processes repeatedly.
:)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top