Avatar of Member_2_3517100
Member_2_3517100
Flag for United Kingdom of Great Britain and Northern Ireland asked on

PowerShell script to carry out action on file generated in a specific folder (works, but 'jumping the gun' sometimes!)

I have a legacy DOS App (no negative comments, there is a long and genuine story to why we are still running this!) that can print to file in PostScript format. I am successfully using the Open Source Ghostscript tool to convert to PDF (specifically calling on the 'ps2pdf' command in the Ghostscript directory, which generates a perfect PDF file).

I've tried creating a PowerShell script to automate this, which constantly monitors a specific folder and if a PostScript (*.ps) file appears in the set directory, works the conversion magic and loads up Adobe Reader with the completed PDF.

Here's the problem; the script only works 9 out of 10 times. I think the problem is the DOS App immediately generates a 0 kb .ps file when printing to file, and then pads out the content after completing generation in memory. It typically takes about 10 seconds to do the actual PostScript content generation, and then writes to the .ps file the content. As far as I can tell, it puts no lock on the file whilst this all happens. I've used the 'FileChanged' type command in my script, so most the time it ignores the initial .ps as it is created, and only does something with it on the second write to file (the change). But sometimes it jumps the gun, and converts to PDF immediately, creating a single blank page from the 0 kb file with nothing in it.

I've tried everything I can think of (or rather, I can find googling) but have failed to reliably fix this. Any ideas?

Here is my script code at present:

$folder = 'c:\Distill'
$filter = '*.ps'
$fsw = New-Object IO.FileSystemWatcher $folder, $filter -Property @{IncludeSubdirectories = $false;}
 
Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged -Action {
cd "C:\Program Files\GPLGS\"     # changes to Ghostscript folder
.\ps2pdf "C:\Distill\Process.ps" "C:\PDFs\Process.pdf"     # runs PS to PDF conversion
del "C:\Distill\Process.ps"     # deletes no longer needed PostScript file
invoke-item "C:\PDFs\Process.pdf"     # loads PDF file into default PDF reader
}

Open in new window


Any thoughts? Whilst I'm an IT Pro, I must admit I'm pretty much a novice when it comes to scripting, certainly PowerShell, so sorry in advance if I'm slow on the uptake at all :-)

Hope someone can help - many thanks in advance
Powershell

Avatar of undefined
Last Comment
Member_2_3517100

8/22/2022 - Mon
footech

How about just introducing a pause?
You could insert the following at line 6, pushing the rest down.
Start-Sleep -seconds 10

Open in new window

Member_2_3517100

ASKER
Thanks for the suggestion, but that's one of the things I already tried at different points inside the curly brackets - I commented it out in the code and didn't bother to include it on the post here to avoid clutter (there are a few comment outs!).

What I (think) I found was it paused some of the activity, but the 'jumping the gun' was still about 1 in 10 times. It's so frustrating, as the other 9 times it works a charm!
footech

Did you extend the pause to something like 60 seconds to see if it's just a timing thing?

I'd put some Write-Host commands in the event action to debug so you can see it working.  The question is if the problem is with whether the event is being triggered correctly, or with some interaction with the DOS app or ps2pdf.  Depending on how the ps2pdf app works, I could see how it could allow execution of the next line before it's done working.

Getting a complete picture of how the DOS app writes its files would be helpful.  For that I would create monitors of Created, Deleted, and Changed events.
https://gallery.technet.microsoft.com/scriptcenter/Powershell-FileSystemWatche-dfd7084b shows good examples.

It might help to set the NotifyFilter to just LastWrite or Size.  If the event is being triggered repeatedly there might be issues.  Notepad for example, when you save a file has two events for LastWrite.
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
Member_2_3517100

ASKER
Many thanks for your reply, and apologies for the delay in counter-replying.

Some days back (before posting on here) I did indeed increase the pause to 30 seconds. the behaviour it exhibited was the same as everything else - 9 times out of 10 it was about 30 seconds from printing in WordStar (the DOS App) and getting the correct output. Then about 1 out of 10, it just fired up the blank PDF straight away. It suggested to me that, when the 'jump the gun' occurs, it disregards any such parameters set.

Thanks for the link to that script - I've read that a lot of times, and used some of the code from it in my own script. I can't recall, but I think I did try implementing that script pretty straight to begin with, but definitely worth a go again to see if it gives any further insight on the PowerShell command line.

Intrigued by idea of altering the NotifyFilter. So is there away I could code in something like "Only process this code if the .ps file size is greater than 5kb in size"? This is where my skill is very novice - any ideas of the code I could try putting in?

Your help is very much appreciated. Cheers! :-)
Member_2_3517100

ASKER
Just to show the results of BigTeddy's script. Below is the output. I count 21 attempts at printing, 2 of them doing a double change as I've experienced (attempts 5 & 7). I'm fast concluding that the DOS App I'm printing from (WordStar) is behaving somewhat erratically, occasionally running a change immediately after a create, even though there is no real data in there yet (it remains at 0kb until the proper second change occurs).

So I suspect the only way to make this work properly is to tie in an additional 'if' parameter to 'FileChanged' to make it only enact if the .ps file is above a certain size. Any thoughts on the code for this?

Still doesn't answer why the 'Start-Sleep' command doesn't work when placed at the beginning of the code just after '-Action  {' . Although, remember I know very little and don't want to to assume anything!

I'll carry on googling along these lines, but if you have an inspiration, it would be massively appreciated :-)

Many thanks :-)  

The file 'PROCESS.PS' was Created at 03/06/2016 21:00:54
The file 'PROCESS.PS' was Changed at 03/06/2016 21:01:01

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:02:40
The file 'PROCESS.PS' was Created at 03/06/2016 21:02:40
The file 'PROCESS.PS' was Changed at 03/06/2016 21:02:47

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:03:11
The file 'PROCESS.PS' was Created at 03/06/2016 21:03:11
The file 'PROCESS.PS' was Changed at 03/06/2016 21:03:18

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:13:27
The file 'PROCESS.PS' was Created at 03/06/2016 21:13:27
The file 'PROCESS.PS' was Changed at 03/06/2016 21:13:35

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:13:43
The file 'PROCESS.PS' was Created at 03/06/2016 21:13:43
The file 'PROCESS.PS' was Changed at 03/06/2016 21:13:44
The file 'PROCESS.PS' was Changed at 03/06/2016 21:13:51

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:14:55
The file 'PROCESS.PS' was Created at 03/06/2016 21:14:55
The file 'PROCESS.PS' was Changed at 03/06/2016 21:15:03

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:15:16
The file 'PROCESS.PS' was Created at 03/06/2016 21:15:16
The file 'PROCESS.PS' was Changed at 03/06/2016 21:15:17
The file 'PROCESS.PS' was Changed at 03/06/2016 21:15:24

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:15:31
The file 'PROCESS.PS' was Created at 03/06/2016 21:15:31
The file 'PROCESS.PS' was Changed at 03/06/2016 21:15:39

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:15:45
The file 'PROCESS.PS' was Created at 03/06/2016 21:15:45
The file 'PROCESS.PS' was Changed at 03/06/2016 21:15:53

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:16:08
The file 'PROCESS.PS' was Created at 03/06/2016 21:16:08
The file 'PROCESS.PS' was Changed at 03/06/2016 21:16:16

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:16:24
The file 'PROCESS.PS' was Created at 03/06/2016 21:16:24
The file 'PROCESS.PS' was Changed at 03/06/2016 21:16:32

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:16:52
The file 'PROCESS.PS' was Created at 03/06/2016 21:16:52
The file 'PROCESS.PS' was Changed at 03/06/2016 21:17:00

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:17:04
The file 'PROCESS.PS' was Created at 03/06/2016 21:17:04
The file 'PROCESS.PS' was Changed at 03/06/2016 21:17:12

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:17:41
The file 'PROCESS.PS' was Created at 03/06/2016 21:17:41
The file 'PROCESS.PS' was Changed at 03/06/2016 21:17:49

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:18:22
The file 'PROCESS.PS' was Created at 03/06/2016 21:18:22
The file 'PROCESS.PS' was Changed at 03/06/2016 21:18:30

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:18:35
The file 'PROCESS.PS' was Created at 03/06/2016 21:18:35
The file 'PROCESS.PS' was Changed at 03/06/2016 21:18:43

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:19:06
The file 'PROCESS.PS' was Created at 03/06/2016 21:19:06
The file 'PROCESS.PS' was Changed at 03/06/2016 21:19:13

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:23:23
The file 'PROCESS.PS' was Created at 03/06/2016 21:23:23
The file 'PROCESS.PS' was Changed at 03/06/2016 21:23:31

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:23:37
The file 'PROCESS.PS' was Created at 03/06/2016 21:23:37
The file 'PROCESS.PS' was Changed at 03/06/2016 21:23:44

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:49:21
The file 'PROCESS.PS' was Created at 03/06/2016 21:49:21
The file 'PROCESS.PS' was Changed at 03/06/2016 21:49:29

The file 'PROCESS.PS' was Deleted at 03/06/2016 21:49:45
The file 'PROCESS.PS' was Created at 03/06/2016 21:49:45
The file 'PROCESS.PS' was Changed at 03/06/2016 21:49:53

Open in new window

Member_2_3517100

ASKER
I read this question poised on StackOverflow: LastWrite FileSystemWatcher Powershell: notification

In the answer, it looks like even Windows Apps like notepad can write double creates or changes to a file, even when essentially only one has occured :-(

I'm now wondering if there is a different approach to this - or indeed if a criteria of a minimum size to the file acting as a trigger might work (e.g. only process script if file is bigger than 5kb). I'm not finding any documentation on how to do that....

I tried running script with just [IO.NotifyFilters]'LastWrite' , but after 10 runs the error occured again.

Thanks again for your input, and look forward to any thoughts you might have :-)
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
footech

What happens when the ghostscript tries to run against a file that isn't there?  I'm wondering if in the case of a double-trigger (or some other collision of circumstances) the file is getting deleted by one event, but another event has already launched another action, and when it gets to the conversion the file is gone.

Here's a sample (untested) with more extensive feedback on what is happening inside the action.  You will be able to see whether the sleep is actually taking place (I can't imagine any reason it wouldn't).  There's also a check for size.  It might be more efficient to have the notifyfilter set to size, rather than having a check in the action.
Register-ObjectEvent $fsw Changed -SourceIdentifier FileChanged -Action {
    Write-Host "$(Get-Date) :: Change detected.  Action started"
    Write-Host "$(Get-Date) :: Sleeping for 10 seconds"
    Start-Sleep -Seconds 10
    cd "C:\Program Files\GPLGS\"     # changes to Ghostscript folder
    If ( Test-Path "C:\Distill\Process.ps" )
    {
        If ( (Get-Item "C:\Distill\Process.ps").length -gt 1000 )
        {
            Write-Host "$(Get-Date) :: Converting file to .PDF"
            .\ps2pdf "C:\Distill\Process.ps" "C:\PDFs\Process.pdf"     # runs PS to PDF conversion
            Write-Host "$(Get-Date) :: Sleeping another 5 seconds"
            Start-Sleep -Seconds 5
            Write-Host "$(Get-Date) :: Deleting source file"
            del "C:\Distill\Process.ps"     # deletes no longer needed PostScript file
            Invoke-Item "C:\PDFs\Process.pdf"     # loads PDF file into default PDF reader
        }
        Else
        {
            Write-Host "$(Get-Date) :: File too small.  Stopping action."
            break
        }
    }
    Write-Host "$(Get-Date) :: Source file not found"
    Write-Host "$(Get-Date) :: Action exiting"
}

Open in new window

Member_2_3517100

ASKER
It's incredibly good of you to be helping me to this extent - many thanks for your reply.

In response:

What happens when the ghostscript tries to run against a file that isn't there?  I'm wondering if in the case of a double-trigger (or some other collision of circumstances) the file is getting deleted by one event, but another event has already launched another action, and when it gets to the conversion the file is gone.
From what I've seen (and apologies, I've probably not explained it very well) I don't think this is happening. When it jumps the gun, it is clear that the file is there and not deleted at that point - but it is an empty file. So it registers in Windows Explorer as a 0kb file called Process.ps. I presume this is the process that WordStar goes through when creating the Process.ps file:

1) Creates empty (0kb) Process.ps file in preparation for adding PostScript generated data to.
2) Generates the PS data in some other temporary location.
3) On completion of generating complete PS data in temp location, WordStar drops it into the Process.ps file, causing a change (I can see from WordStar this happens as soon as 'Printing' disappears within the DOS App).

What happens when it all goes wrong is point 1 occurs, WordStar still has 'Printing' in the app, but the script acknowledges an immediate change after creation of PS file, reflected in the output I got from BigTeddy's script, pasted earlier. WordStar then finishes printing, and if I've closed the blank PDF generated quickly enough, it does then correctly generate the complete PDF (otherwise, there is a lock on the PDF as it is open in Adobe Reader, and nothing happens).

Many thanks for your script code - I've implemented that and it works, but I will keep running it at random intervals all day to see if it trips up at all. It will be great if it doesn't. Can I ask, does the line:

If ( (Get-Item "C:\Distill\Process.ps").length -gt 1000 )

Open in new window

define that the Process.ps file has to be certain data size in order to run? I tried an If statement nested in an If statement like this before, but with my lacking knowledge didn't know about the '.length' parameter. If it is what I think it is, that could be the absolute answer.

A lot of people online have said the IO.FileSystemWatcher is quite inconsistent as to what it detects as a change, but if a condition of a minimum file size is set, then that (in my mind) would resolve it, and which I think is probably the code you've given me above. Most people suggested using WMI to achieve the objective was more accurate, but I looked at code for this and was immediately completely out of my depth!

Thanks so much for your help :-)
ASKER CERTIFIED SOLUTION
footech

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
Member_2_3517100

ASKER
Hi - many thanks for your feedback.

If what you say is true about .PDF and Adobe Reader, then just removing the Invoke-Item command should avoid the issue.
Agreed, although this is an important aspect to have running. My co-workers need a solution that brings up the PDF once done - if it relied on them going to a folder and manually opening after a certain time period, I can imagine all sorts of confusion and support calls (they are not wonderfully tech savvy). And they would have a point that it wouldn't be as efficient as it loading up as a PDF for them to physically print (or otherwise) automatically.

I'm still not 100% convinced that the issue as I described wouldn't happen with the original code.  Just for my own curiosity, what does happen when you try to run the ghostscript against a non-existent file?  In any case, what I posted should avoid that scenario.
No probs - I didn't quite interpret your request properly before, think I follow now. What I should have mentioned, in all my testings of the script, is I have commented out any deletion commands / events previously, and still the problem with the 'jumping the gun' has happened. Now I understand your request, will try this shortly to quench any curiosity!!

Yes, that line does check the size of the file (in bytes).  You could change the size as you see fit.
And feel free to confirm (or refute) that the Start-Sleep is working as expected.   Once we get something working, the sleep command can probably be removed.
That's exactly what I hoped you'd say. When the error happens, the initial PostScript file that is blank is always 0kb in size. So having something that (rounded) is looking for a 1kb file or higher is perfect. Typically, these PS files are not smaller than 5kb in size. So this is the golden bit of code I've been searching for, and it looks to be working in the PowerShell script you kindly wrote for me. I'm going to implement this into a nested 'IF' statement in my original script, and see if I can make it work. Was already dropping the sleep command, but saw the value in you putting that in for testing, as it showed the mechanics of the script running with pauses.

I will post back when I've run the test with Ghostscript, and done multiple tests with my own script with the file size condition added - it looks very hopeful, and thanks again for your invaluable input!
Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
footech

Checking back.  How's it coming?
Member_2_3517100

ASKER
Hi Footech.

Really sorry for lack of feedback. I found that once I implemented the various IF statements like your script, in my own, the problem still occurred. Then there were some other IT troubles at work that completely pulled my attention away, but I will be getting back to this again asap (this week for sure).

Will reply properly in due course - many thanks :-)
Member_2_3517100

ASKER
Hi Footech.

I think I have finally worked out what's occurring here. I would like to test my theory extensively, so will go through the tedious process of 100 manual prints from WordStar later today to see if I'm right. So I'll be back on here later - it's 02:15am in the UK, so calling it a night whilst I'm winning - but wanted to engage so you know this isn't question abandoned.

Hopefully, I'll come back with a full explanation and be issuing points later - thanks :-)
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
Member_2_3517100

ASKER
Hi Footech.

This is what I've concluded:

I don't know the correct terminology, but the method provided by MS that assesses whether a file has changed may be a little questionable. A few things I read online point to this.

Equally, and possibly in addition, WordStar may be doing inconsistent things with how it is producing the initial PostScript file, which it ultimately fills up with data once WordStar finishes printing. This could be why the PowerShell script randomly detects a change when I perceive there isn't one - because there actually is a small change written to the file system, but it's random.

Finally, the most important point. I realised that when WordStar generates the initial 0kb PS file, it isn't actually 0kb! It was showing up in Windows as this, but I managed to do a quick refresh and copy, and it is actually about 5kb in size. The information immediately written on creation contains I guess what you might term the header info, which is then added to when the printing finishes. So, using your incredibly helpful -gt switch, even when the error occurs, it now blocks processing below a certain size, and carries on waiting. Thanks so much for that!

I'm tidying up my script, will test it works, and then will post it on here and award points :-)

Many thanks.
footech

Sounds like you've made some good progress.  This appears to be one of those cases where behavior may not always be consistent.  Gathering enough data so you can see behavior (and any variations that can occur) is the biggest step towards a resolution.

Glad I could help.
Member_2_3517100

ASKER
The key bit of info I needed was the line in the script:

If ( (Get-Item "C:\Distill\Process.ps").length -gt 1000 )

This is what I'd been looking for via Google, with the 1000 representing minimum number of bytes for allowing the IF statement to run. As it turned out, for my script I needed to set this to about 5000, as the files being generated weren't 0kb as first thought, but about 4.5kb!

Thank you very much footech - you have been an awesome help, and I couldn't have resolved this problem without your input. Many thanks! :-)

P.S. Still refining my actual script - will post it on here when I'm happy with the final :-)
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck