Author Posts

April 27, 2016 at 5:48 am

Hi Guys

Need a little help here.
I have a system that generates pdf files and stores them in a few different subfolders under the same parent folder.

I need theese files printed out and then moved to a subfolder within their respective folders.
Filestructure looks like this:

Parent |
|- subfolder1 |
| |printed
|
|-subfolder2 |
| |printed
|
|-subfolder3 |
|printed

I found the script below which watches the parent folder (including subfolders) and prints whenever a new pdf is generated in one of those. This works fine.

But... I cannot get the file moved from ex. subfolder1 to "subfolder1>printed" afterwards.

Current script:

$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
 $FileSystemWatcher.Path = “C:\DocsToPrint\Files”
 $FileSystemWatcher.IncludeSubdirectories = $true

Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action {
$folderpath = Get-Item $Event.SourceEventArgs.FullPath
  if((Get-Item $Event.SourceEventArgs.FullPath | select -Expand Extension) -eq “.pdf”)
 {
Start-Process -FilePath $Event.SourceEventArgs.FullPath -Verb Print -PassThru | %{ sleep 10;$_ } | kill
 }
 Move-Item -Path $folderPath.path -Filter *.pdf -Destination ($folderpath + "\" + 'printed')
 Out-File -FilePath c:\DocsToPrint\outlog.txt -Append 
 }

Problems: Move-Item not working, no log output (just added 2 min ago for troubleshooting).
Also I should probably make sure that the "printed" folders are not watched, so that I don't get a print from there too.

Any help will be greatly appreciated. 🙂

Best Regards,
Jesper

April 27, 2016 at 7:23 am

So, you probably _don't_ want to move the files to a subfolder in the same hierarchy, or you'll trigger the Watcher again. Keep that in mind.

Also keep in mind that you don't need to concatenate paths like you're doing, and that the Join-Path command is a better way of doing so.

$destination = Join-Path $folderpath "printed"

Are you getting any errors? My supposition is that your -Destination path isn't correctly formed, which my suggestion will correct, but without any kind of errors it's a little tough to troubleshoot.

April 28, 2016 at 1:45 am

Hi Don
Thanks a lot for the reply. I have never heard of join-path before. But I can see that it seems like the right way to go. I'll see if I can make that work. 🙂

Regarding providing errors, I would love to give you some, but I am not getting any?which is why I was trying the out-file log thing.
I was thinking if this failing could be due to the script kind of "never ending", cause it is always active (watching for new files). Don't know if I am right in that assumption?

When I run the script in ISE, I just get this:

PS C:\Users\jeskri> P:\Powershell scripts\PDF_Printout3.ps1

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command                  
--     ----            -------------   -----         -----------     --------             -------                  
2      ce4b160a-791...                 NotStarted    False                                ...                      



PS C:\Users\jeskri> 

No output what so ever, when dragging pdf files to the folders... or any errors from the move-item command which is not working.

I have been searching a little bit for "the right way" to get errorlogs from your scripts, but there seem to be a lot of different ways to do it. PSLogging, the transscipt way or write-debug etc? any suggestions?

/Jesper

April 28, 2016 at 4:32 am

well looking through your code, your out-file appears to have nothing passed to it.

i would do something like this (assuming you take don's advice and use the join-path into a $destination variable)

try
{
 Move-Item -Path $folderPath.path -Filter *.pdf -Destination ($folderpath + "\" + 'printed')
"file moved to $destination"| Out-File -FilePath c:\DocsToPrint\outlog.txt -Append
}
catch
{
$error|Out-File  -FilePath c:\DocsToPrint\outlog.txt -Append
$Error.Clear()
}

essentially wrap the move into a try/catch, if successful output to the logfile

and if an error occurs send the error details to the logfile

April 28, 2016 at 6:25 am

Hi David

Thanks for pitching in. I tried your suggestion and it seems I am getting closer to a solution.

With your help the error logging part is now working, so I have some output to troubleshoot with.

The Script now looks like this:

$FileSystemWatcher = New-Object System.IO.FileSystemWatcher
 $FileSystemWatcher.Path = “C:\DocsToPrint\Files”
 $FileSystemWatcher.IncludeSubdirectories = $true
 
Register-ObjectEvent -InputObject $FileSystemWatcher -EventName Created -Action {
  if((Get-Item $Event.SourceEventArgs.FullPath | select -Expand Extension) -eq “.pdf”)
 {
Start-Process -FilePath $Event.SourceEventArgs.FullPath -Verb Print -PassThru | %{ sleep 10;$_ } | kill
 }
 $folderpath = Get-Item $Event.SourceEventArgs.FullPath
  try
{
 Move-Item -Path $folderPath.path -Filter *.pdf -Destination ($folderpath + "\" + 'printed')
"file moved to $destination"| Out-File -FilePath c:\DocsToPrint\outlog.txt -Append
}
catch
{
$error|Out-File  -FilePath c:\DocsToPrint\outlog.txt -Append
$Error.Clear()
}
 }

There is something wrong with the $folderpath variable and I admit that I am not at all sure where in the script to place that variable? I tried placing it in the top with the other variables, but that gives an error in the console:
Get-Item : Cannot bind argument to parameter 'Path' because it is null.

Placing it where it is now in the above script, gives a similar error in the errorlog.
Outlog.txt gives this error:

Move-Item : Cannot bind argument to parameter 'Path' because it is null.
At P:\Powershell scripts\PDF_Printout6.ps1:13 char:18
+  Move-Item -Path $folderPath.path -Filter *.pdf -Destination ($folderpath + "\"  ...
+                  ~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Move-Item], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.MoveItemCommand
 
Method invocation failed because [System.IO.FileInfo] does not contain a method named 'op_Addition'.
At P:\Powershell scripts\PDF_Printout6.ps1:13 char:2
+  Move-Item -Path $folderPath.path -Filter *.pdf -Destination ($folderpath + "\"  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (op_Addition:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

I guess it is pretty obvious that I don't know what the hell I am doing half the time. But I'm learning and think it is exciting, so that is a positive thing.

/Jesper

April 28, 2016 at 7:29 am

well i've never used filesystemwatcher personally, but it looks like whatever is contained in $folderpath = Get-Item $Event.SourceEventArgs.FullPath

the .path value is not valid, i'd explore exactly what is contained within $folderpath

easiest is to put $folderpath.path|out-file -filepath c:\DocsToPrint\outlog.txt -append
in your catch statement.

that will let you see exactly what is being passed into your move-item

April 28, 2016 at 11:24 pm

Thanks again David

that gives me this error:

Method invocation failed because [System.IO.FileInfo] does not contain a method named 'op_Addition'.
At P:\Powershell scripts\PDF_Printout6.ps1:13 char:2
+ Move-Item -Path $folderPath.path -Filter *.pdf -Destination ($folderpath + "\" ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound

/Jesper

April 29, 2016 at 12:40 am

Man, I feel like I am SO close.

It seems that this:
$folderpath = Get-Item $Event.SourceEventArgs.fullpath

...contains both the path AND the filename. So it includes the file name in the path.

Outlog says:
file moved to C:\DocsToPrint\Files\Outgoing\xxx.pdf\printed

I tried to not use the "fullpath" property (if it is infact a property?):
$folderpath = Get-Item $Event.SourceEventArgs.path
...but that doesn't work. Not sure how to get a list of which properties can be used for $Event.SourceEventArgs ??

/Jesper

April 29, 2016 at 4:36 am

someone else will need to chime-in, i do not know exactly what all is contained in the event args.

April 29, 2016 at 6:11 am

Hi David

I know what you mean. My knowledge of Powershell, Objects, properties and so on is simply not deep enough for me to solve this on my own. and since I am from Denmark, once in a while, my understanding of English sometimes add to the confusion as well. 🙂
I have been googeling all afternoon to find something usefull.

I found and tried this: $FileSystemWatcher | gm -membertype *

which gives me:

TypeName: System.IO.FileSystemWatcher

Name                      MemberType Definition                                                                                                                                                 
----                      ---------- ----------                                                                                                                                                 
Changed                   Event      System.IO.FileSystemEventHandler Changed(System.Object, System.IO.FileSystemEventArgs)                                                                     
Created                   Event      System.IO.FileSystemEventHandler Created(System.Object, System.IO.FileSystemEventArgs)                                                                     
Deleted                   Event      System.IO.FileSystemEventHandler Deleted(System.Object, System.IO.FileSystemEventArgs)                                                                     
Disposed                  Event      System.EventHandler Disposed(System.Object, System.EventArgs)                                                                                              
Error                     Event      System.IO.ErrorEventHandler Error(System.Object, System.IO.ErrorEventArgs)                                                                                 
Renamed                   Event      System.IO.RenamedEventHandler Renamed(System.Object, System.IO.RenamedEventArgs)                                                                           
BeginInit                 Method     void BeginInit(), void ISupportInitialize.BeginInit()                                                                                                      
CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)                                                                                            
Dispose                   Method     void Dispose(), void IDisposable.Dispose()                                                                                                                 
EndInit                   Method     void EndInit(), void ISupportInitialize.EndInit()                                                                                                          
Equals                    Method     bool Equals(System.Object obj)                                                                                                                             
GetHashCode               Method     int GetHashCode()                                                                                                                                          
GetLifetimeService        Method     System.Object GetLifetimeService()                                                                                                                         
GetType                   Method     type GetType()                                                                                                                                             
InitializeLifetimeService Method     System.Object InitializeLifetimeService()                                                                                                                  
ToString                  Method     string ToString()                                                                                                                                          
WaitForChanged            Method     System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherChangeTypes changeType), System.IO.WaitForChangedResult WaitForChanged(System.IO.WatcherC...
Container                 Property   System.ComponentModel.IContainer Container {get;}                                                                                                          
EnableRaisingEvents       Property   bool EnableRaisingEvents {get;set;}                                                                                                                        
Filter                    Property   string Filter {get;set;}                                                                                                                                   
IncludeSubdirectories     Property   bool IncludeSubdirectories {get;set;}                                                                                                                      
InternalBufferSize        Property   int InternalBufferSize {get;set;}                                                                                                                          
NotifyFilter              Property   System.IO.NotifyFilters NotifyFilter {get;set;}                                                                                                            
Path                      Property   string Path {get;set;}                                                                                                                                     
Site                      Property   System.ComponentModel.ISite Site {get;set;}                                                                                                                
SynchronizingObject       Property   System.ComponentModel.ISynchronizeInvoke SynchronizingObject {get;set;}

Which seems useful.

But this:
$Folderpath | gm -membertype *

...doesn't work.

gm : You must specify an object for the Get-Member cmdlet.
At line:1 char:15
+ $Folderpath | gm -membertype *
+               ~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Get-Member], InvalidOperationException
    + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand

probably because $Event.SourceEventArgs is not an object that can be piped to gm or??

I did find another thread on another forum, where NotifyFilter is marketed.
Any idea if that would be a better way to go when trying to get where I want?

http://stackoverflow.com/questions/20558212/powershell-how-to-mointor-a-directory-and-move-files-over-to-another-folder

/Jesper

May 2, 2016 at 7:20 am

FINALLY, It works... 🙂

Current script:

# Variables for Watcher
$folder = "C:\DocsToPrint\Files"
$filter = '*.pdf'

# Watcher + Settings                     
$fsw = New-Object IO.FileSystemWatcher $folder, $filter 
$fsw.IncludeSubdirectories = $true              
$fsw.NotifyFilter = [IO.NotifyFilters]'FileName', 'DirectoryName'

# Register Event (when file is created)
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {

# Foreach file loop - print pdf file
ForEach ($f in $fsw) {
if(($File = Get-Item $Event.SourceEventArgs.FullPath | select -Expand Extension) -eq “.pdf”) {
   Start-Process -FilePath $Event.SourceEventArgs.FullPath -Verb Print -PassThru | %{ sleep 10;$_ } | kill

# Variables for move   
$folderpath = ($Event.SourceEventArgs.FullPath | Split-Path)
$folderfile = ($Event.SourceEventArgs.FullPath | Split-Path -Leaf)
$destination = "C:\DocsToPrint\Printed"
     }

# Variables for logging
$logpath =  'C:\DocsToPrint\outlog.txt'

# Grab current file and move to "printed" folder
try
{
   Get-ChildItem -Path $folderpath -Filter $folderfile| Move-Item -Destination $destination | Out-File -FilePath $logpath -Append

# Log move in logfile   
   "file $folderfile moved to $destination"| Out-File -FilePath $logpath -Append
   }

# Log if errors + clear
catch
{
$error|Out-File -FilePath $logpath -Append
$Error.Clear()
   }
   }
   }

This is probably not the "cleanest" code in history, but it get the job done.

Two questions!
If a file with the same name already exists in "printed" folder, it doesn't move the files. I would like to rename the newest file by adding a number and then move. Ideas?

I would like to place the script in the startup folder on the server, so it executes the ps1 script after reboot etc. I created a .cmd file with these entries:

powershell -command Set-executionpolicy Unrestricted
powershell -command ". C:\temp\PDF_Printout13.ps1"

...but that doesn't Work, like the script isn't running/active?

Any help or suggestions to make current script better will be greatly appreciated.

Best Regards, Jesper