How to register an event in Powershell so it stays permanent? - powershell

I have the below script, that is just supposed to monitor a folder, and when a .xlsx file is created, then it should convert it to a .csv file in another folder. It works on the first file created, but it doesn't work on the next file being created in the folder. How can I get the event to stay permanent?
$ErrorActionPreference = 'Stop'
$folder = 'c:\Users\exgtcl\hotfolder'
$destination = 'c:\Users\exgtcl\targetfolder'
$filter = '*.xlsx'
Function Convert-Csv
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true)][String]$FullPath
)
$ExcelFiles = Get-ChildItem $FullPath
Write-Host "Working on file '$FullPath' "
$excelApp = New-Object -ComObject Excel.Application
$excelApp.DisplayAlerts = $false
$excelApp.Visible = $false
$workbook = $excelApp.Workbooks.Open($ExcelFiles.FullName)
$newName = "$destination\output.csv"
$workbook.SaveAs($newName, [Microsoft.Office.Interop.Excel.XlFileFormat]::xlCSV)
$workbook.Close()
# Release Excel Com Object resource
$excelApp.Workbooks.Close()
Start-Sleep 2
$excelApp.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excelApp) | Out-Null
}
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$action = {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Convert-Csv -FullPath $path
Start-Sleep 3
Write-Host "moving files"
Move-Item $path -Destination $destination -Force -Verbose
}
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action $action

Related

PowerShell IO.FileSystemWatcher doesn't work after restart fileserver

I need a FileSystemWatcher on a network directory (fileserver)
The script works well but fails after a reboot from the fileserver.
How can I detect if the FSW is failing and restart the watcher if the fileserver is up and running again?
Code:
$destinations = #{"\\location1" = "c:\destination1"
"\\location2" = "c:\destination2"
}
foreach ($location in $destinations.Keys) {
$Watcher = New-Object IO.FileSystemWatcher -Property #{
Path = $location
Filter = "*.*"
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
Register-ObjectEvent $Watcher -EventName Created -SourceIdentifier $location -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$SI = $Event.SourceIdentifier
Write-Host "The file '$name' was $changeType at $timeStamp"
Write-Host $path
Move-Item $path -Destination $destinations[$SI] -Force -Verbose
}
}
RTFM!
There is Error event from FileSystemWatcher, and there is your case exactly described.
For example, if the object is monitoring changes in a remote directory and the connection to that directory is lost, the Error event is raised.
Solution found:
$destinations = #{"\\location1" = "c:\destination1"
"\\location2" = "c:\destination2"
}
foreach ($location in $destinations.Keys) {
$Watcher = New-Object IO.FileSystemWatcher -Property #{
Path = $location
Filter = "*.*"
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$action = {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$SI = $Event.SourceIdentifier
Write-Host "The file '$name' was $changeType at $timeStamp"
Write-Host $path
Move-Item $path -Destination $destinations[$SI] -Force -Verbose
}
$actionError = {
$Sender.EnableRaisingEvents = $false
$ip = $location -split "\\" | Where { $_ -ne "" } | Select -first 1
do {
Write-Host "Waiting for boot " + $ip
Start-Sleep -Seconds 5
} until (Test-Connection $ip -Quiet -Count 1)
$Sender.EnableRaisingEvents = $true
}
Register-ObjectEvent $Watcher -EventName Created -SourceIdentifier $location -Action $action
Register-ObjectEvent $Watcher -EventName Error -SourceIdentifier $location+'ERROR' -Action $actionError
}

How to make a io.systemfileswatcher run indefinitely

I have a script that calls two io.systemfile watchers which I would like to run 24x7 so that it can detect data that needs to be processed and automatically hand it off to a script that processes it (and uploads the result to the network):
Function Report1Watcher{
param ($folder)
$filter = "*vac*.csv"
$watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $true
EnableRaisingEvents = $true
InternalBufferSize = 65536
}
Write-Host "Watching $folder for creation of $filter files..."
$changeAction = {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$Actionpath = Split-Path $Path
write-host $Actionpath " Ready for Report1"
Report1 $Actionpath
write-host $Actionpath " Report1 Generated"
}
Register-ObjectEvent $Watcher -EventName "Created" -Action $changeAction
}
Function Report2Watcher{
param ($folder)
$filter = "*4.4*"
$watcher = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $true
EnableRaisingEvents = $true
InternalBufferSize = 65536
}
Write-Host "Watching $folder for creation of $filter files..."
$changeAction = {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
$Actionpath = Split-Path $Path
$Actionpath = Split-Path $Actionpath
write-host $Actionpath " Ready for Report2"
Report2 $Actionpath
write-host "Report2 Generated"
}
Register-ObjectEvent $Watcher -EventName "Created" -Action $changeAction
}
Function Watchmaster{
Report1Watcher $Testdir
Report2Watcher $Testdir
}
Watchmaster
I am thinking of turning it into a windows service, but would it be able to run 24x7 as is? I'm not sure where I could add an infinite loop without creating infinite io.systemfilewatchers. I know as a PS script that powershell would need to constantly be running. But if I created a batch file that runs this and set it up as a service with nssm would this accomplish what I need?

Difference action for different files

I want to monitor new files created in a folder.
When this happens I want to launch a batch file (in the example below I just write a line in a log file).
I don't know why this doesn't work.
My code is:
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "D:\"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
$action = {
$path = $Event.SourceEventArgs.FullPath
$changeType = $Event.SourceEventArgs.ChangeType
$logline = "$(Get-Date), $changeType, $path"
if ($file.Name -like "Apertura") {
Add-Content "C:\Users\F701845\Desktop\Apertura.txt" -Value $logline
} else {
Add-Content "C:\Users\F701845\Desktop\TestNO.txt" -Value $logline
}
}
Register-ObjectEvent $watcher "Created" -Action $action
while ($true) {sleep 5}
Well this one is easy, you are using a variable that is empty in your IF/ELSE.
$path and $changeType are derived from $Event but $file is not existing at all.
First take a look at what you have and you will see that you might be able to use in this case: $Event.SourceEventArgs.Name.
$Event.SourceEventArgs | Get-Member
bool Equals(System.Object obj)
int GetHashCode()
type GetType()
string ToString()
System.IO.WatcherChangeTypes ChangeType {get;}
string FullPath {get;}
string Name {get;}
While it does work, it still looks for files called exactly Apertura meaning Apertura.txt wont work, i would recommend using something like Apertura.* if you do not know the extension.
Example Code:
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\test\"
$watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
$action = {
$path = $Event.SourceEventArgs.FullPath
$changeType = $Event.SourceEventArgs.ChangeType
$file = $Event.SourceEventArgs.Name #get filename from Event data
$logline = "$(Get-Date), $changeType, $path"
if ($file -like "Apertura.*") { #removed the .Name and added .*
Add-Content "C:\Users\username\Desktop\Apertura.txt" -Value $logline
} else {
Add-Content "C:\Users\username\Desktop\TestNO.txt" -Value $logline
}
}
Register-ObjectEvent $watcher "Created" -Action $action
while ($true) {sleep 5}
$watcher.Dispose() #can be used to dispose the System.IO.FileSystemWatcher

Powershell : FileSystemWatcher for filename change

I tried using this code to monitor filename changes on our fileserver. But I only want to trigger the event when the extension changes.
Any suggentions?
### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "E:\"
# $watcher.Filter = "*.*"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
$action = { $path = $Event.SourceEventArgs.FullPath
$changeType = $Event.SourceEventArgs.ChangeType
$Name = $Event.SourceEventArgs.Name
$logline = "$(Get-Date), $changeType, $path, $name"
Add-content "E:\log.txt" -value $logline
}
Register-ObjectEvent $watcher "Renamed" -Action $action
while ($true) {sleep 5}
Set the NotifyFilter property in the $watcher variable to FileName only.
$watcher.NotifyFilter = 'FileName'
Now the action will only trigger for file name changes.

FileSystemWatcher detect when file is moved to folder

I'm having trouble with a script that's monitoring a folder.
FileSystemWatcher only seems to detect when a file is being copied to the folder, not when it's just being moved to it from the same drive.
Is there a way to detect this?
$desFolder = "H:\Media"
$ExFolder = "H:\Monitor"
$Filter = '*.*'
$fsw = New-Object IO.FileSystemWatcher $ExFolder, $Filter
$fsw.IncludeSubdirectories = $true
$fswOnChange = Register-ObjectEvent -InputObject $fsw -EventName Changed -SourceIdentifier FileUpdated -Action {
$File = Get-Item $EventArgs.FullPath
if($File.Name -imatch '\.(?:mp4|mkv)$' -and $file.Name -match '.*?S\d+E+'){
Start-Sleep -s 1
if(Test-FileReady $File.FullName){
Move-Files $File
}
}
}
function global:Test-FileReady {
Param([parameter(Mandatory=$true)][string]$Path)
if (Test-Path -LiteralPath $Path) {
trap {
return $false
}
$stream = New-Object system.IO.StreamReader $Path
if ($stream) {
$stream.Close()
return $true
}
}
}
function global:Move-Files{
Param([parameter(Mandatory=$true)][System.IO.FileInfo]$File)
Write-Host $File.Name
}
Try using Renamed and Created events as well.
BTW, the IO.FileSystemWatcher docs say:
The component will not watch the specified directory until the Path is set, and EnableRaisingEvents is true
$fsw.EnableRaisingEvents = $true