Note: Running PowerShell v3
I've got a directory setup that is currently:
\ftproot\001\converted
\ftproot\001\inbound
\ftproot\001\pdf
\ftproot\002\converted
\ftproot\002\inbound
\ftproot\002\pdf
\ftproot\xxx\converted
\ftproot\xxx\inbound
\ftproot\xxx\pdf
Each FTP user is mapped to an inbound directory. The structure can be changed if this makes the solution easier
\ftproot\converted\001
\ftproot\converted\002
\ftproot\converted\xxx
\ftproot\inbound\001
\ftproot\inbound\002
\ftproot\inbound\xxx
\ftproot\pdf\001
\ftproot\pdf\002
\ftproot\pdf\xxx
I need to monitor each inbound directory for TIFF files and pickup new FTP users and inbound directories as they are added (following the same structure), so I'm not looking to modify the script for each new user.
The script is currently like:
$fileDirectory = "\ftproot";
$folderMatchString = "^[0-9]{3}$";
$inboundDirectory = "inbound";
$filter = "*.tif";
$directories = dir -Directory $fileDirectory | Where-Object {$_.Name -match $folderMatchString}
foreach ($directory in $directories) {
$sourcePath = "$fileDirectory\$directory\$inboundDirectory"
if(Test-Path -Path $sourcePath -pathType container ) {
// Problem is here //
$fsw = New-Object IO.FileSystemWatcher $sourcePath, $filter -Property #{
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
}
}
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file '$name' was $changeType at $timeStamp"
}
I'm wrapping the New-Object IO.FileSystemWatcher in a loop so in the case of the above example, only the last directory will be monitored.
Is it possible to create an array or list or similar of IO.FileSystemWatcher? And if so, how can I code the Register-ObjectEvent for each instance? There will be over a hundred directories to monitor in the end (and slow increasing) but the same action applies to all inbound folders.
Or is there a better solution I should investigate?
The process is the same for each FTP user, the files need to say within directories that can be linked back to the user. The inbound directory will be monitored for uploaded TIFF files, when a file is detected a multi page TIFF is split into individual files and moved into the converted directory, when a new file is detected in converted another action is performed on the TIFF file, then it is converted to PDF and moved to the PDF folder.
Thank you
Solution
$result = #($directoriesOfInterest | ? { Test-Path -Path $_ } | % {
$dir = $_;
$fsw = New-Object IO.FileSystemWatcher $dir, $filter -Property #{
IncludeSubdirectories = $false
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
};
$oc = Register-ObjectEvent $fsw Created -Action {
$path = $Event.SourceEventArgs.FullPath;
$name = $Event.SourceEventArgs.Name;
$changeType = $Event.SourceEventArgs.ChangeType;
$timeStamp = $Event.TimeGenerated;
Write-Host "The file '$name' was $changeType at $timeStamp";
};
new-object PSObject -Property #{ Watcher = $fsw; OnCreated = $oc };
});
Is it possible to create an array or list or similar of IO.FileSystemWatcher?
Yes. Making use of a pipeline would be something like:
$fsws = $directoriesOfInterest | ? { Test-Path -Path $_ } | % {
new-object IO.FileSystemWatcher …
}
and $fsws will be an array, if there is more than one watcher created (it will be a waster if there is only one, $null if there are none). To always get an array put the pipeline in #(…):
$fsws = #($directoriesOfInterest | ? { Test-Path -Path $_ } | % {
new-object IO.FileSystemWatcher …
})
if so, how can I code the Register-ObjectEvent for each instance?
Put the object registration in the same loop, and return both the watcher and the event registration:
$result = #($directoriesOfInterest | ? { Test-Path -Path $_ } | % {
# $_ may have a different value in code in inner pipelines, so give it a
# different name.
$dir = $_;
$fsw = new-object IO.FileSystemWatcher $dir, …;
$oc = Register-ObjectEvent $fsw Created …;
new-object PSObject -props #{ Watcher = $fsw; OnCreated = $oc };
})
and $result will be an array of objects will those two properties.
Note the -Action code passed to Register-ObjectEvent can reference $fsw and $dir and thus know which watcher has fired its event.
Related
Please find below script, what it does is find *.txt files and rename with either filename.host1.txt or filename.host2.txt.
What my requirement is it should create in sequence mode and not in random (Currently it is creating Host1 or host2 as random basis and not in sequence mode), If first file it renames as file1.host1.txt, then second file should be file2.host2.txt,and third file will be file3.host3.txt, fourth will be again starts with file4.host1.txt,file5.host2.txt,file4.host3.txt,.
Basically from i want to add "host1" to "host3" whenever new file detects in folder and create files in sequential mode from 1 to 3 only. Host1,Host2,Host3 i used three virtual machine to execute sperete script.
Here is my script..
### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\test"
$watcher.Filter = "*.txt"
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true
### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = { $path = $Event.SourceEventArgs.FullPath
$filename = [io.path]::GetFileNameWithoutExtension($path)
###$changeType = $Event.SourceEventArgs.ChangeType
###$logline = "$(Get-Date), $changeType, $path, $filename"
###Add-content "D:\log.txt" -value $logline
###$proc=start-process "D:\source\$filename.bat" -Wait -NoNewWindow
$script:counter++
$hostname=""
if(($script:counter % 2) -eq 0){
$hostname="host1"
} Else {
$hostname="host2"
}
Rename-Item -Path "C:\test\$filename.txt" -NewName "$filename.$hostname.txt"
}
### DECIDE WHICH EVENTS SHOULD BE WATCHED
Register-ObjectEvent $watcher "Created" -Action $action
### Register-ObjectEvent $watcher "Changed" -Action $action
### Register-ObjectEvent $watcher "Deleted" -Action $action
### Register-ObjectEvent $watcher "Renamed" -Action $action
while ($true) {sleep 5}
You can Register a new ObjectEvent after renaming a file, so you would prepare for the next file that is coming. If a new file get created while its renaming a file. You will miss a file to rename. Chances of this happeningen is close to zero.
### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\test"
$watcher.Filter = "*.txt"
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true
### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = { $path = $Event.SourceEventArgs.FullPath
$filename = [io.path]::GetFileNameWithoutExtension($path)
###$changeType = $Event.SourceEventArgs.ChangeType
###$logline = "$(Get-Date), $changeType, $path, $filename"
###Add-content "D:\log.txt" -value $logline
###$proc=start-process "D:\source\$filename.bat" -Wait -NoNewWindow
$script:counter++
$hostname=""
if(($script:counter % 2) -eq 0){
$hostname="host1"
} Else {
$hostname="host2"
}
Rename-Item -Path "C:\test\$filename.txt" -NewName "$filename.$hostname.txt"
# ADD this line
Register-ObjectEvent $watcher "Created" -Action $action
}
### DECIDE WHICH EVENTS SHOULD BE WATCHED
Register-ObjectEvent $watcher "Created" -Action $action
OR
You could do it this way, it will run slower and proccess multiple files at the same time. It will look at the creation date to determine which file was created first. Note that if you move a file here the creationdate of the file is not updated and it will use the original creation date of the file.
code:
$counter = 1
while($True){
# get all the files in C:\ test, filter out the ones that are already renamed and sort them on creation time
# if a file is moved into this directory you will not have the creationtime will be the time that it was originally created and not the moved time
Get-ChildItem -Path c:\test | ? {$_.FullName -notlike "*.host*.txt"} | Sort-Object -Property CreationTime | % {
#determine the suffix
if($counter % 3 -eq 0){
$hostSuffix = 3
}elseif($counter % 3 -eq 2){
$hostSuffix = 2
}elseif($counter % 3 -eq 1){
$hostSuffix = 1
}
# renaming file
$filename = "$($_.BaseName).host$($hostSuffix).txt"
write-host "#$($counter): renaming: $($_.FullName) to: $filename"
Rename-Item $_.FullName -NewName $filename
#count
$counter++
}
sleep(4)
}
Im using this script to scan a folder for the created files. Sadly this are temp files and gets created and deleted by a program we use.
Code:
$folder = 'C:\Users\JuanMa\Desktop\RestBar\RestBar'
$filter = '*.*' # <-- set this according to your requirements
$destination = 'C:\Users\JuanMa\Desktop\UNB'
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $true # <-- set this according to your requirements
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file '$name' at '$path'was $changeType at $timeStamp"
Copy-Item $path -Destination $destination -Force -Verbose # Force will overwrite files with same name
}
The Script its not being able to copy the file to the destination folder. Im assuming that the files gets deleted before it can be copied. Its there a way to lock the file so it dosent get deleted but after the copy operation?
Thanks!
I have a measuring device on a PC in our network. The PC is not joined to the domain, however has a shared folder for which I have a username and password.
I am attempting to build a Powershell script to monitor this folder from a remote server, for new CSV files, the script will then copy the file to a specified folder location. I am struggling to pass through the parameters to the FileSystemWatcher cmdlet.
Any ideas?
$folder = '\\remoteip\folder\subfolder'# <--'<full path to the folder to watch>'
$filter = '*.csv'
$destination = '\\mynetworkstorage\folder\' # <--' Where is the file going?
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubdirectories = $true # <-- set this according to your requirements
NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file '$name' was $changeType at $timeStamp"
Copy-Item $path -Destination $destination -Force -Verbose
}
EDIT -
The script will be run from a server joined to our domain, so there is a need to pass through credentials to the folder in order that I can access it. I have these credentials.
I refactored the code a little (mainly so I could more easily understand it):
$folder = '\\localhost\c$\tmp'
$filter = '*.*'
$destination = '\\localhost\c$\tmp_destination\'
$fsw = New-Object IO.FileSystemWatcher
$fsw.Path = $folder
$fsw.Filter = $filter
$fsw.IncludeSubdirectories = $true
$fsw.EnableRaisingEvents = $true
$fsw.NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
$action = {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host "The file '$name' was $changeType at $timeStamp"
Copy-Item $path -Destination $destination -Force -Verbose
}
$created = Register-ObjectEvent $fsw Created -Action $action
while ($true) {sleep 1}
I ran this code locally and managed to get files created in $folder automatically copied to $destination.
FileSystemWatcher run in the context of the current user. In the case of accessing remote systems that require login, impersonation is required.
If the remote system only got local users, no domain user that the source system can log on as, then impersonation does not seem to be possible.
See this and this link for more details on impersonation.
I am using powershell 3. need to monitor a folder, if there are any image files, move them over to antoher folder.
here's my code, i test it, its not working, couldn't figure out what need to be fixed.
#<BEGIN_SCRIPT>#
#<Set Path to be monitored>#
$searchPath = "F:\download\temp"
$torrentFolderPath = "Z:\"
#<Set Watch routine>#
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $searchPath
$watcher.IncludeSubdirectories = $false
$watcher.EnableRaisingEvents = $true
$created = Register-ObjectEvent $watcher "Created" -Action {
Copy-Item -Path $searchPath -Filter *.jpg -Destination $torrentFolderPath –Recurse
}
#<END_SCRIPT>#
UPDATE:
I got it partially working. still have one issue left. lets start with an empty folder. I download an image (1.jpg) to the folder, nothing moved to Z: drive. then I download another image (2.jpg) to the folder. 1.jpg will be moved to Z: drive. seems like the newly created one never get moved over.
$folder = "F:\\download\\temp"
$dest = "Z:\\"
$filter = "*.jpg"
$fsw = new-object System.IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubDirectories=$false
NotifyFilter = [System.IO.NotifyFilters]'FileName, LastWrite'
}
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
Move-Item -Path F:\download\temp\*.jpg Z:\
}
You have not registered the NotifyFilter. That is why your code is not working.
here is a sample which registers the NotifyFilter and prints the file details which was created
$folder = "c:\\temp"
$filter = "*.txt"
$fsw = new-object System.IO.FileSystemWatcher $folder, $filter -Property #{
IncludeSubDirectories=$false
NotifyFilter = [System.IO.NotifyFilters]'FileName, LastWrite'
}
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceVentArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Write-Host $path
Write-Host $name
Write-Host $changeType
Write-Host $timeStamp
}
Event action scripts run in a separate scope that only has access to global variables, so depending on your implementation you have have issues trying to use those variables in the action script. One way around without resorting to declaring global variables (bad mojo!) is to use an expandable string to create a script block with the variables expanded before you register the event:
$ActionScript =
[Scriptblock]::Create("Copy-Item -Path $searchPath -Filter *.jpg -Destination $torrentFolderPath –Recurse")
$created = Register-ObjectEvent $watcher "Created" -Action $ActionScript
I created a PowerShell script to monitor a folder for new files; it deletes files containing "cmr" and logs the names of the files that contains "cdr".
This all worked yesterday and today I decided to reboot and see if the event will stay but I can't even get it to work at all, I am not sure what happened.
$folder = "C:\Users\home\Documents\calldata"
$filter = '*.*'
Set-Location $folder
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}
Register-ObjectEvent $fsw Created -SourceIdentifier NewCallData -Action{
$name = $Event.SourceEventArgs.Name
if($name -match "cmr"){
Write-Host $folder\$name
Remove-Item $folder\$name
}
if($name -match "cdr"){
Out-File -FilePath C:\MCallPowershell\outlog.txt -Append -InputObject "$name"
}
}
You might need to un-register your event to run a new instance:
Unregister-Event NewCallData
then run it again