Filewatcher - catching multiple events issue - powershell

I have a script that watches for files and triggers events on things like change. It works except there are duplicate events triggered for the same file (previous Stackflow question) which I need to handle. I am writing the filename to a logfile and only re-logging it if it is not in there.
The problem is I think the events are being fired synchronously and I am getting 2 entries in the log for 1 file change even though I am checking for dups. I am not sure how to work around this.
Here is the code:
Unregister-Event FW*
$source = 'S:\FIS-BIC Reporting\Report Output Files\IT\'
$filter = '*.*'
$MasterFile = "H:\PS\EmailNotifications.csv"
$LogFile = "H:\EmailNotify.log"
$FilesToCheck = import-csv -path $Masterfile
if (! (Test-Path $Logfile))
{
$Output = "Date|Filename|FilenameSpec|Email"
$Output | Out-File $LogFile
}
function Get-CharRight($str,$chars)
{
return $str.Substring($str.Length – $chars, $chars)
}
function ProcessFile()
{
param ([string]$filename)
Write-Host "file: $filename"
$ext= (Get-CharRight $filename 3)
if ($ext -ne "tmp")
{
foreach($FileToCheck in $FilesToCheck)
{
if($filename -like $FileToCheck.FilenameSpec)
{
Write-Host "Match"
#check log to see if an email was already sent
$Exists = import-csv -path $Logfile -delimiter "|" | where-object {$_.Filename -eq $filename}
Write-Host ($Exists.Count -eq $null)
if ($Exists.Count -eq $null)
{
Write-Host "Not in logfile"
$Date = Get-Date -format G
$Output = $Date + "|" + $filename + "|" + $FileToCheck.FilenameSpec + "|" + $FileToCheck.Email
$Output | Out-File $LogFile -append
}
else
{write-host "duplicate"}
}
}
}
}
$fsw = New-Object IO.FileSystemWatcher $source, $filter -Property #{IncludeSubdirectories = $true; NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite, Attributes'}
Register-ObjectEvent $fsw Changed -SourceIdentifier FWFileChanged -Action {
ProcessFile $Event.SourceEventArgs.FullPath
}
Register-ObjectEvent $fsw Created -SourceIdentifier FWFileCreated -Action {
ProcessFile $Event.SourceEventArgs.FullPath
}
while($true) {
start-sleep -s 2
}
`

Related

Powershell - When watching folder for new file created - if statement inside "Register-ObjectEvent" is not firing

Goal: File watcher watches the staging folder for new files. If the new file name contains "JDE_Reimb" then move the file into another folder "$reimb_jde_path" ,also renaming the new file.
Issue: The If statement is not firing me at all. Up to creating the $fullpath works, but my If statement is not executing. Any ideas why? Also I need to watch this folder for 2 other file which contain specific string in the file name. What is the best way to accomplish this in one script?
$reimbFile = "JDE_Reimb"
$reimb_jde_path = "\\rootfolder\reimbfolder"
$staging_folder_path = "\\rootfolder\stagingfolder\" #"
$filter = "*.txt"
$watcherCRExport_Staging = New-Object IO.FileSystemWatcher $staging_folder_path, $filter -Property #{
IncludeSubdirectories = $false
}
Register-ObjectEvent $watcherCRExport_Staging Created -SourceIdentifier foldertest -Action {
$text = $eventArgs.Name
Write-Host $text# Write-Host $eventArgs.ChangeType
$fullpath = Join-Path -Path $staging_file_path -ChildPath $text# Write-Host "Full path is: $fullpath"
If($text.Contains($reimbFile)) {
$newtext = $text.Substring(0, $text.IndexOf("_") + 1 + $text.Substring($text.IndexOf("_") + 1).IndexOf("_"))# Write-Host "Created: $($newtext)"
$newfilename = $newtext + ".txt"
$nextpath = Join-Path -Path $reimb_jde_path -ChildPath $newtext
Copy-Item-Path $fullpath -Destination $nextpath
Write-Host "---------------------------------"
Write-Host $newfilename
Write-Host $fullpath
Write-Host $nextpath
Write-Host "Reimbursement Done copying"
Write-Host "---------------------------------"
}
Else {
Write-Host "No Match!"
}
}

Powershell Script not working after inclusion of Log Function

Please find below my powershell script that is used to move files from one folder to another :
$folder = 'C:\test'
$filter = '*.*'
$directory_source = $directory_source = 'C:\test1\*' # <-- set this according to your requirements
$directory_target_cop = 'C:\Folder1'
$directory_target_prop = 'C:\Folder2'
$directory_target_dom = 'C:\Folder3'
$LogTime = Get-Date -Format "MM-dd-yyyy"
$dtNow = Get-Date -Format "MM-dd-yyyy_hh-mm-ss"
$script:loggingpathpreference = 'C:\PSLogs\'+$LogTime+".log"
Set-PSBreakpoint -Variable Now -Mode Read -Action {Set-Variable Now (get-date -uformat '%Y\%m\%d %H:%M:%S') -Option ReadOnly, AllScope -Scope Global -Force -ErrorAction SilentlyContinue} -ErrorAction SilentlyContinue
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $true # <-- set this according to your requirements
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
Function Write-Log {
[cmdletbinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $true)]
[ValidateNotNull()]
$Message,
[switch]$Warning
)
Process{
if($Message -isnot [string]){
$Message = $Message | Out-String
}
Write-Verbose $message
Add-Content -Path $script:LoggingPathPreference -Value "`n$($dtNow) : $($Message)"
if($Warning){
Write-Warning $Message
}
}
}
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Log "The file '$name' was $changeType at $timeStamp"
try{
$files = $(ls $directory_source)
foreach ($file in $files) {
if ( $(gc $file | Select-String "PROP" -Quiet) ) {
mv -Path $file.Fullname -Destination $directory_target_prop
} elseif ( $(gc $file | Select-String "COP" -Quiet) ) {
mv -Path $file.Fullname -Destination $directory_target_cop
} elseif ( $(gc $file | Select-String "DOM" -Quiet) ) {
mv -Path $file.Fullname -Destination $directory_target_dom
}
}
}catch{
$ErrorMessage = $_.Exception.Message
Write-Log $ErrorMessage
}
}
Now if I remove my Write-Log function then the above script moves the files from test folder to respective three folders, but when I do include the Write-Log function, the above code is not able to move the files.
The above code is doing the logging but not moving the files.
What am I doing wrong here?
Looking forward to your solutions and thanks in advance.

powershell file system event watcher service

I have a file transformation solution built upon powershell with filesystemwatcher and powershell noexit. The problem I have is that the events don't fire after a while (I have no idea when it stops, I just get a call from the production), and I'm not sure why that is.
The question is basically if/how I can check health of existing active eventwatchers. I tried first by adding control if event object existed so that I could simply execute the powershell script a few times a day, but soon figured out it was powershell session bound and I was not able to fetch it from a new powershell session.
This is the basic structure of the solution.
(Anonymized, so functions etc renamed a bit and the workload logics is stripped out and on the places where it says '...' it is not syntax error)
powershell.exe -NoExit -ExecutionPolicy Bypass -inputformat none -File C:\|...|.ps1 WINDOWSTARTUP="hidden" WAITFORINPUT="NO" DOS="YES"
function Test-FileLock {
param ([parameter(Mandatory=$true)][string]$Path)
$oFile = New-Object System.IO.FileInfo $Path
if ((Test-Path -Path $Path) -eq $false)
{
return $false
}
try
{
$oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
if ($oStream)
{
$oStream.Close()
}
$false
}
catch
{
# file is locked by a process.
return $true
}
}
function ConvertType1($sourceFile, $custno, $destFile)
{
$result = "-1"
try {
$OFS = "`r`n"
$data = ""
$data += "..."
$data += $OFS + "`tType=""..."""
# |...|
$data | Out-File $destFile
$result = "Done"
Write-Host $result
}
catch
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$result = "ConvertType1: " + $ErrorMessage + " - " + $FailedItem
Write-Host $result
}
finally
{
}
return $result
}
function ConvertType2($sourceFile, $custno, $destFile)
{
$result = "-1"
try {
$OFS = "`r`n"
$data = ""
$data += "..."
$data += $OFS + "`tType=""..."""
# |...|
$data | Out-File $destFile
$result = "Done"
Write-Host $result
}
catch
{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$result = "ConvertType2: " + $ErrorMessage + " - " + $FailedItem
Write-Host $result
}
finally
{
}
return $result
}
#$devTest = $true
$devTest = $false
if ($devTest)
{
$logfile = "....LOG"
$monitorFolder = '\\..._transformation\Development\Test\In' # Enter the root path you want to monitor.
$destinationFolder = '\\..._transformation\Development\Test\Out' # Enter the root path you want to monitor.
$filter = '*.*' # You can enter a wildcard filter here.
}
else
{
$logfile = ".....LOG"
$monitorFolder = '\\..._transformation\In' # Enter the root path you want to monitor.
$destinationFolder = '\\..._transformation\Out' # Enter the root path you want to monitor.
$filter = '*.*' # You can enter a wildcard filter here.
}
$fsw = New-Object IO.FileSystemWatcher $monitorFolder, $filter -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}
#Check if eventsubcriber already exists, if it does just terminate script. This way we can schedule this script continously to make sure we always have the watcher in place
#Added note: This does not work because the events are per session and we won't be able to see events from previous sessions.
if($devTest)
{
if(Get-EventSubscriber | Where-Object -Property SourceIdentifier -eq "devTest_...FileCreated") {
Exit
}
Register-ObjectEvent $fsw Created -SourceIdentifier devTest_...FileCreated -Action {
$sourceFilename = $Event.SourceEventArgs.Name
$sourcePathFilename = $Event.SourceEventArgs.FullPath
$sourcePath = split-path $sourcePathFilename
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$destinationFile = (join-path $destinationFolder ([system.io.fileinfo]$sourceFilename).BaseName) + ".wli"
Write-Host "$logTime - The file '$sourcePathFilename' was $changeType at $timeStamp" -fore green
try {
$logTime = (Get-Date -format (Get-culture).DateTimeFormat.UniversalSortableDateTimePattern)
Out-File -FilePath $logfile -Append -InputObject "$logTime - The file '$sourceFilename' was $changeType at $timeStamp"
do {
sleep -seconds 1
$fileLockTest = Test-FileLock $sourcePathFilename
} while ($fileLockTest)
switch ($sourceFilename.ToLower())
{
{($_ -like "*hire*") -or ($_ -like "*change*")} { $result = ConvertType1 $sourcePathFilename 123456 $destinationFile }
{($_ -like "*termination*") -or ($_ -like "*...*")} { $result = ConvertType2 $sourcePathFilename 123456 $destinationFile }
default {$result = "Error. File name is not recognized. File name must |...|"}
}
Write-Host "$logTime - Transformation of file '$sourceFilename': $result"
Out-File -FilePath $logfile -Append -InputObject "$logTime - Transformation of file '$sourceFilename': $result"
Move-Item $sourcePathFilename (join-path $sourcePath "Backup") -force
}
Catch
{
$logTime = (Get-Date -format (Get-culture).DateTimeFormat.UniversalSortableDateTimePattern)
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
Move-Item $sourcePathFilename (join-path $sourcePath "Error") -force
Write-Host "$logTime - The file '$sourceFilename' encountered an error during transformation. The error message was $ErrorMessage" -fore red
Out-File -FilePath $logfile -Append -InputObject "$logTime - The file '$sourceFilename' encountered an error during transformation. The error message was $ErrorMessage"
}
}
}
else
{
if(Get-EventSubscriber | Where-Object -Property SourceIdentifier -eq "...FileCreated") {
Exit
}
Register-ObjectEvent $fsw Created -SourceIdentifier ...FileCreated -Action {
$sourceFilename = $Event.SourceEventArgs.Name
$sourcePathFilename = $Event.SourceEventArgs.FullPath
$sourcePath = split-path $sourcePathFilename
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$destinationFile = (join-path $destinationFolder ([system.io.fileinfo]$sourceFilename).BaseName) + ".wli"
Write-Host "$logTime - The file '$sourcePathFilename' was $changeType at $timeStamp" -fore green
try {
$logTime = (Get-Date -format (Get-culture).DateTimeFormat.UniversalSortableDateTimePattern)
Out-File -FilePath $logfile -Append -InputObject "$logTime - The file '$sourceFilename' was $changeType at $timeStamp"
do {
sleep -seconds 1
$fileLockTest = Test-FileLock $sourcePathFilename
} while ($fileLockTest)
switch ($sourceFilename.ToLower())
{
{($_ -like "*hire*") -or ($_ -like "*change*")} { $result = ConvertType1 $sourcePathFilename 123456 $destinationFile }
{($_ -like "*termination*") -or ($_ -like "*...*")} { $result = ConvertType2 $sourcePathFilename 123456 $destinationFile }
default {$result = "Error. File name is not recognized. File name must include information about type of file |...|"}
}
Write-Host "$logTime - Transformation of file '$sourceFilename': $result"
Out-File -FilePath $logfile -Append -InputObject "$logTime - Transformation of file '$sourceFilename': $result"
Move-Item $sourcePathFilename (join-path $sourcePath "Backup") -force
}
Catch
{
$logTime = (Get-Date -format (Get-culture).DateTimeFormat.UniversalSortableDateTimePattern)
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
Move-Item $sourcePathFilename (join-path $sourcePath "Error") -force
Write-Host "$logTime - The file '$sourceFilename' encountered an error during transformation. The error message was $ErrorMessage" -fore red
Out-File -FilePath $logfile -Append -InputObject "$logTime - The file '$sourceFilename' encountered an error during transformation. The error message was $ErrorMessage"
}
}
}

Monitor files with FileSystemWatcher

I'm trying to create a PowerShell program to monitor when a file in a certain path is modified, and only if it now contains the string "loaded successfully", to Write-Host. Eventually an email component will be added if the string is not found in a certain amount of time.
$folder = 'C:\Users\afila\Desktop\TEST' # Enter the root path you want to monitor.
$files = Get-ChildItem $folder
$filter = '*.txt' # You can enter a wildcard filter here.
# In the following line, you can change 'IncludeSubdirectories to $true if required.
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $false;
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
# Function to search folder contents for a string
function Search-Folder {
$SearchTerm = "Loaded successfully"
foreach ( $file in $files) {
$text = Get-Content "$folder\$file"
if ($text | where {$text -like "*$SearchTerm*" }) {
Write-Host "The file '$file succeeded" -fore white
Out-File -FilePath C:\Users\afila\Desktop\SucceededLog.txt -Append -InputObject "The file '$file' completed at $timeStamp"
}
}
}
# Here, all the Changed event is registerd.
Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged -Action {
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$searchTerm = "Loaded successfully"
foreach ($file in $files) {
$text = Get-Content "$folder\$file"
if ($text | where {$text -like "*$searchTerm*"}) {
Write-Host "The file '$name' was $changeType at $timeStamp" -fore magenta
Out-File -FilePath C:\Users\afila\Desktop\TestLogItems\TestLog.txt -Append -InputObject "The file '$name' was $changeType at $timeStamp"
}
}
}
# To stop the monitoring, run the following commands:
Unregister-Event FileChanged
The FileSystemWatcher already gives you the full path to the file via the property $Event.SourceEventArgs.Path, so you don't need to loop over a list of files.
Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged -Action {
$path = $Event.SourceEventArgs.Path
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$searchTerm = 'Loaded successfully'
if ((Get-Content $path) -like "*$searchTerm*"}) {
Write-Host "The file '$name' was $changeType at $timeStamp"
}
}

Powershell - "The process cannot access the file because it is being used by another process"

Below is a script that monitors a directory and its subfolders for deposited files. Every 10 minutes or so, I look for new files and then match them against a database table that tell me where they need to be moved to - then it copies the files to a local archive, moves them to the locations they need to be moved to, and inserts a record into another database table with the file's attributes and where it came and went. If there is no match in the database - or there is an script error - it sends me an email.
However, since files are getting deposited to the directory constantly, it's possible that a file is still being written when the script executes. As a result, I get the error The process cannot access the file because it is being used by another process. emailed to me all the time. In addition, because I'm not dealing with the error up front; it goes through the loop and a false entry is inserted into my log table in the database with incorrect file attributes. When the file finally frees up, it gets inserted again.
I'm looking for a way to identify files that have processes attached to them; and skipping them when the script executes - but several days of web searches and some testing hasn't yielded an answer yet.
## CLEAR ERROR LOG
$error.clear()
Write-Host "***File Transfer Script***"
## PARAMETERS
$source_path = "D:\Files\In\"
$xferfail_path = "D:\Files\XferFailed\"
$archive_path = "D:\Files\XferArchive\"
$email_from = "SQLMail <SQLMail#bar.com>"
$email_recip = [STRING]"foo#bar.com"
$smtp_server = "email.bar.com"
$secpasswd = ConvertTo-SecureString "Pa$$w0rd" -AsPlainText -Force
$smtp_cred = New-Object System.Management.Automation.PSCredential ("BAR\SQLAdmin", $secpasswd)
## SQL LOG FUNCTION
function Run-SQL ([string]$filename, [string]$filepath, [int]$filesize, [int]$rowcount, [string]$xferpath)
{
$date = get-date -format G
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=SQLSERVER;Database=DATABASE;Uid=SQLAdmin;Pwd=Pa$$w0rd;"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "INSERT INTO DATABASE..Table VALUES ('$date','$filename','$filepath',$filesize,$rowcount,'$xferpath',0)"
$SqlCmd.Connection = $SqlConnection
$SqlCmd.ExecuteNonQuery()
$SqlConnection.Close()
}
## DETERMINE IF THERE ARE ANY FILES TO PROCESS
$file_count = Get-ChildItem -path $source_path |? {$_.PSIsContainer} `
| Get-ChildItem -path {$_.FullName} -Recurse | Where {$_.psIsContainer -eq $false} | Where {$_.Fullname -notlike "D:\Files\In\MCI\*"} `
| Measure-Object | Select Count
If ($file_count.Count -gt 0)
{
Write-Host $file_count.Count "File(s) Found - Processing."
Start-Sleep -s 5
## CREATE LIST OF DIRECTORIES
$dirs = Get-ChildItem -path $source_path -Recurse | Where {$_.psIsContainer -eq $true} | Where {$_.Fullname -ne "D:\Files\In\MCI"} `
| Where {$_.Fullname -notlike "D:\Files\In\MCI\*"}
## CREATE LIST OF FILES IN ALL DIRECTORIES
$files = ForEach ($item in $dirs)
{
Get-ChildItem -path $item.FullName | Where {$_.psIsContainer -eq $false} | Sort-Object -Property lastWriteTime -Descending
}
## START LOOPING THROUGH FILE LIST
ForEach ($item in $files)
{
## QUERY DATABASE FOR FILENAME MATCH, AND RETURN TRANSFER DIRECTORY
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=SQLSERVER;Database=DATABASE;Uid=SQLAdmin;Pwd=Pa$$w0rd;"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = "SELECT F.DirTransfer FROM DATABASE..Files F WHERE '$item.Name.Trim()' LIKE F.FileName"
$SqlCmd.Connection = $SqlConnection
$DirTransfer = $SqlCmd.ExecuteScalar()
$SqlConnection.Close()
If ($DirTransfer) # if there is a match
{
Write-Host $item.FullName"`t->`t"$DirTransfer
$filename = $item.Name
$filepath = $item.FullName
$filesize = $item.Length
If (!($filesize))
{
$filesize = 0
}
$rowcount = (Get-Content -Path $item.FullName).Length
If (!($rowcount))
{
$rowcount = 0
}
$xferpath = $DirTransfer
Run-SQL -filename "$filename" -filepath "$filepath" -filesize "$filesize" -rowcount "$rowcount" -xferpath "$DirTransfer"
Copy-Item -path $item.FullName -destination $DirTransfer -force -erroraction "silentlycontinue"
Move-Item -path $item.FullName -destination $archive_path -force -erroraction "silentlycontinue"
#Write-Host "$filename $filepath $filesize $rowcount $xferpath"
}
Else # if there is no match
{
Write-Host $item.FullName "does not have a mapping"
Move-Item -path $item.FullName -destination $xferfail_path -force
$filename = $item.FullName
$email_body = "$filename `r`n`r`n does not have a file transfer mapping setup"
Send-MailMessage -To $email_recip `
-From $email_from `
-SmtpServer $smtp_server `
-Subject "File Transfer Error - $item" `
-Body $email_body `
-Priority "High" `
-Credential $smtp_cred
}
}
}
## IF NO FILES, THEN CLOSE
Else
{
Write-Host "No File(s) Found - Aborting."
Start-Sleep -s 5
}
## SEND EMAIL NOTIFICATION IF SCRIPT ERROR
If ($error.count -gt 0)
{
$email_body = "$error"
Send-MailMessage -To $email_recip `
-From $email_from `
-SmtpServer $smtp_server `
-Subject "File Transfer Error - Script" `
-Body $email_body `
-Priority "High" `
-Credential $smtp_cred
}
You can use the SysInternals handles.exe to find the open handles on a file. The exe can be downloaded from http://live.sysinternals.com/.
$targetfile = "C:\Users\me\Downloads\The-DSC-Book.docx"
$result = Invoke-Expression "C:\Users\me\Downloads\handle.exe $targetfile" | Select-String ([System.IO.Path]::GetFileNameWithoutExtension($targetfile))
$result
Outputs:
WINWORD.EXE pid: 3744 type: File 1A0: C:\Users\me\Downloads\The-DSC-Book.docx
Alternatively, you can check for errors either via try/catch or by looking at the $error collection after the Move-Item attempt then handle the condition appropriately.
$error.Clear()
Move-Item -path $item.FullName -destination $xferfail_path -force -ea 0
if($error.Count -eq 0) {
# do something useful
}
else {
# do something that doesn't involve spamming oneself
}
To expand on Arluin's answer. It fails if there's spaces in either the handle.exe or the $targetfile.
This will work for spaces in both and also formats the result to give you the Program Name.exe
$targetfile = "W:\Apps Folder\File.json"
$result = & "W:\Apps (Portable)\handle.exe" "$targetfile" | Select-String ([System.IO.Path]::GetFileNameWithoutExtension($targetfile))
$result = $result -replace '\s+pid\:.+'
$result
# PS> FreeCommander.exe
One way to avoid file locks caused by running the script on a timer is to use an event driven approach using a file system watcher. It has the ability to execute code when an event such as a new file is created in the folder you are monitoring.
To run code when the file is finished copying you would need to listen for the changed event. There is a slight issue with this event in that it fires once when the file begins copying and again when it is finished. I got an idea to work around this chicken/egg problem after checking out the module Mike linked to in the comments. I've updated the code below so that it will only fire off code when file has fully been written.
To try, change $folderToMonitor to the folder you want to monitor and add some code to process the file.
$processFile = {
try {
$filePath = $event.sourceEventArgs.FullPath
[IO.File]::OpenRead($filePath).Close()
#A Way to prevent false positive for really small files.
if (-not ($newFiles -contains $filePath)) {
$newFiles += $filePath
#Process $filePath here...
}
} catch {
#File is still being created, we wait till next event.
}
}
$folderToMonitor = 'C:\Folder_To_Monitor'
$watcher = New-Object System.IO.FileSystemWatcher -Property #{
Path = $folderToMonitor
Filter = $null
IncludeSubdirectories = $true
EnableRaisingEvents = $true
NotifyFilter = [System.IO.NotifyFilters]'FileName,LastWrite'
}
$script:newFiles = #()
Register-ObjectEvent $watcher -EventName Changed -Action $processFile > $null