none
Powershell Ordnerüberwachung

    Frage

  • Hallo, ich bin noch ein Neuling mit Powerscript und möchte folgendes Problem lösen:

    In einem zu überwachenden Ordner werden aperiodisch Dateien mit der Endung .job abgelegt, die auf Abholung warten.

    Also z.B.  "R1234567.job"

    Zu irgendeinem Zeitpukt wird von einem anderen Prozess eine Datei gleichen Namens mit der Endung .pdf (oder .tif ode wie auch immer) ebenfalls dort abgelegt, also in dem FAll "R1234567.pdf".

    Das Uberwachungsscript soll in regelmäßigen Abständen den Ordner überprüfen und feststellen, ob zu einer Job-Datei eine zugehörige PDF-Datei identischen Namens vorhanden ist.

    Dann, und nur dann, werden die beiden zusammengehörigen Dateien in einen Zielordner verschoben und im überwachten Ordner gelöscht.

    Es ist immens wichtig, dass das Verschieben immer nur als Paar erfolgen darf, keinesfalls jede Datei einzeln, sobald sie im überwachten Ordner vorliegt.

    Das heißt, die Job-Datei muss sozusagen auf ihre PDF-Datei warten, bis beide in den Zielordner gelangen dürfen.

    Mit einfachen Scriptbefehlen kriege ich das bis jetzt nicht gebacken.

    Wäre für jeden Hinweis dankbar.

    Jonathan

    Donnerstag, 6. April 2017 09:11

Antworten

  • ...Verbesserungsvorschlag: Filtern immer im Provider, wenn der das unterstützt, nicht in der Pipeline.
     
    >     $JobsToCopy = Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"} | select -ExpandProperty FullName
     
    Get-ChildItem -Path $( $Path + '\*.job' )
     
    Das wäre dann auch nicht mehr case sensitiv :)
     
    Oder so wie Christoph vorgeschlagen hat, gefällt mir auch sehr gut.
     
    Donnerstag, 6. April 2017 13:58
  • Hallo Jonathan,

    fand das eigentlich ganz interessant, daher hab ich mal was gebaut, was Dir helfen könnte.

    $Path = "T:\Location\Test\"
    $Destination4DoneJobs = "T:\Location\Ready\"
    
    $CountJobs = (Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"}).Count
    
    while ($CountJobs -gt 0) {
        $JobsToCopy = Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"} | select -ExpandProperty FullName
        
        foreach ($Job in $JobsToCopy) {
            $JobsPDF = ($Job.Replace(".job",".pdf"))
            if (Test-Path "$JobsPDF") {
                
                Move-Item "$Job" "$Destination4DoneJobs" -Force
                $JustWait = timeout 1 /nobreak
                if (Test-Path ($Job.Replace("$Path","$Destination4DoneJobs"))) {
                    Write-Host "Successfully copied $Job" -ForegroundColor Green
                    }
                Move-Item "$JobsPDF" "$Destination4DoneJobs" -Force
                if (Test-Path ($JobsPDF.Replace("$Path","$Destination4DoneJobs"))) {
                    Write-Host "Successfully copied $JobsPDF" -ForegroundColor Green
                    }
                }
            }
        $JustHide = timeout 5 /nobreak
        $CountJobs = (Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"}).Count
        
        }
    

    Das Script läuft solange mindestens eine Datei mit Endung ".job" im Ordner liegt. Danach beendet es sich nach ca. 5 Sekunden. Natürlich kannst Du die While-Schleife auch raus nehmen und das Script über die Aufgabenplanung alle X Minuten ausführen lassen.

    MfG

    Jannik D.

    Donnerstag, 6. April 2017 10:49
  • Ich hätte dann auch noch einen Vorschlag:

    $QuellOrdner = "PfadZuDeinemStartordner"
    $ZielOrdner = "PfadZuDeinemZielordner"
    
    $Dateien = Get-ChildItem $QuellOrdner
    
    $DateienGruppiert = $Dateien | group{$_.BaseName}
    
    foreach($g in ($DateienGruppiert | where{$_.Count -gt 1} ))
    {
        $Extensions = $g.Group | select -ExpandProperty Extension
        if($Extensions -contains ".job")
        {
            $g.Group | foreach{Copy-Item $_.Fullname $ZielOrdner}
        }
    }

    Zunächst bestimmen wir alle Dateien im Startordner und gruppieren diese nach ihrem BaseName (der Dateiname ohne Endung). Danach wird über jene Gruppen geschleift, die mehr als eine Datei enthalten. Damit sind schon einmal alle einzelnen Dateien weg.

    Jetzt wird noch nachgesehen, ob in der Gruppe eine .job-Datei liegt. Wenn das der Fall ist, werden alle Dateien der Gruppe kopiert. Es ist an dieser Stelle irrelevant, welche Endung die Dateien haben und es können auch mehr als zwei sein. 

    Und noch ein Tipp für Janniks Skript: Der -replace Operator verwendet RegularExpressions, die Replace-Methode jedoch nicht. 
    $Job.Replace(".job",".pdf")
    $Job -replace "\.job", "\.pdf"
    Der Vorteil ist, das der -replace Operator per Default nicht zwischen Groß/Kleinschreibung unterscheidet.

    Donnerstag, 6. April 2017 11:47

Alle Antworten

  • Hallo Jonathan,

    prinzipiell sei schon einmal gesagt, dass das kein schweres Skript ist. Du wirst es also mit wenigen Codezeilen hinbekommen. Auf jeden Fall solltest du dich mit PowerShell-Grundlagen befassen und insbesondere wirst du hierfür die Comdlets Get-Childitem, Copy-Item und Where-Object brauchen. Vielleicht auch noch Test-Path, das ist eine Designfrage

    Das Ablauf wäre der: Du bestimmst Dateien in deinem Ordner mit Get-Childitem. Dann suchst du als Pipeline mit where nach denjenigen Dateien, für die eine .pdf-Datei vorhanden ist. Für diese Dateien führst du dann den Kopiervorgang aus, wobei du einfach alle Dateien mit dem gleichen BaseName kopierst.

    Viele Grüße

    Christoph

    Donnerstag, 6. April 2017 09:38
  • Hallo Christoph,

    danke erst mal für die schnelle Antwort.

    Das prinzipielle Herangehen ist mir jetzt klar.

    Nun muss ich mal rauskriegen, wie ich das umsetzen kann.

    Lesen und probieren, das übt. Schaun wir mal ...

    Donnerstag, 6. April 2017 10:14
  • Hallo Jonathan,

    fand das eigentlich ganz interessant, daher hab ich mal was gebaut, was Dir helfen könnte.

    $Path = "T:\Location\Test\"
    $Destination4DoneJobs = "T:\Location\Ready\"
    
    $CountJobs = (Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"}).Count
    
    while ($CountJobs -gt 0) {
        $JobsToCopy = Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"} | select -ExpandProperty FullName
        
        foreach ($Job in $JobsToCopy) {
            $JobsPDF = ($Job.Replace(".job",".pdf"))
            if (Test-Path "$JobsPDF") {
                
                Move-Item "$Job" "$Destination4DoneJobs" -Force
                $JustWait = timeout 1 /nobreak
                if (Test-Path ($Job.Replace("$Path","$Destination4DoneJobs"))) {
                    Write-Host "Successfully copied $Job" -ForegroundColor Green
                    }
                Move-Item "$JobsPDF" "$Destination4DoneJobs" -Force
                if (Test-Path ($JobsPDF.Replace("$Path","$Destination4DoneJobs"))) {
                    Write-Host "Successfully copied $JobsPDF" -ForegroundColor Green
                    }
                }
            }
        $JustHide = timeout 5 /nobreak
        $CountJobs = (Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"}).Count
        
        }
    

    Das Script läuft solange mindestens eine Datei mit Endung ".job" im Ordner liegt. Danach beendet es sich nach ca. 5 Sekunden. Natürlich kannst Du die While-Schleife auch raus nehmen und das Script über die Aufgabenplanung alle X Minuten ausführen lassen.

    MfG

    Jannik D.

    Donnerstag, 6. April 2017 10:49
  • Hallo, Yannik, das ist ja große Klasse, dass du da gleich mit einem kompletten Script kommst.

    Da hätte ich lange basteln können.

    Ich habe es mal getestet und folgendes Ergebnis:

    Ausgangssituation:

    Im Ordner Z:\Test befinden sich die Dateien

    BS 35535.JOB

    L160417. JOB und

    L160417.PDF

    Letztere sollten also, weil als Paar komplett vorhanden, verschoben werden, die erste (BS 35535.JOB) noch nicht, da ihr Pendant fehlt.

    Ergebnis der Scriptausführung ist Folgendes:

    Successfully copied Z:\Test\BS 35535.JOB
    Successfully copied Z:\Test\L160417.JOB
    Move-Item : Der Pfad "Z:\Test\L160417.JOB" kann nicht gefunden werden, da er nicht vorhanden ist.
    Bei Zeile:18 Zeichen:22
    +             Move-Item <<<<  "$JobsPDF" "$Destination4DoneJobs" -Force
        + CategoryInfo          : ObjectNotFound: (Z:\Test\L160417.JOB:String) [Move-Item], ItemNotFoundException
        + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.MoveItemCommand

    und im Ordner Z:\Ready

    befinden sich jetzt die beiden JOB-Dateien, während die PDF-Datei noch im TEST-Ordner liegt.

    Mit der Ursachenforschung bin ich im Moment noch überfordert, aber ich versuche jetzt mal zu verstehen, wie dein Script arbeitet!

    Danke erst mal für deine Hilfe. Vielleicht hast du ja schneller raus, was da abgeht!

    Jonathan

    Donnerstag, 6. April 2017 11:06
  • Hallo Jonathan,

    der Fehler ist bei mir reproduzierbar und tritt nur dann auf, wenn die Endung des Dateinamens groß geschrieben wird. Ändere im Script bitte in Zeile 4, 7, 10, 25 die jeweiligen Endungen in Großbuchstaben um. Ich hoffe, die Dateien werden konsequent mit .PDF und .JOB erstellt. Ich würde zudem zwischen Zeile 18 und 19 nochmal eine Wartezeit einbauen, wie es in Zeile 14 der Fall ist.

    MfG

    Jannik D.

    Donnerstag, 6. April 2017 11:30
  • Ich hätte dann auch noch einen Vorschlag:

    $QuellOrdner = "PfadZuDeinemStartordner"
    $ZielOrdner = "PfadZuDeinemZielordner"
    
    $Dateien = Get-ChildItem $QuellOrdner
    
    $DateienGruppiert = $Dateien | group{$_.BaseName}
    
    foreach($g in ($DateienGruppiert | where{$_.Count -gt 1} ))
    {
        $Extensions = $g.Group | select -ExpandProperty Extension
        if($Extensions -contains ".job")
        {
            $g.Group | foreach{Copy-Item $_.Fullname $ZielOrdner}
        }
    }

    Zunächst bestimmen wir alle Dateien im Startordner und gruppieren diese nach ihrem BaseName (der Dateiname ohne Endung). Danach wird über jene Gruppen geschleift, die mehr als eine Datei enthalten. Damit sind schon einmal alle einzelnen Dateien weg.

    Jetzt wird noch nachgesehen, ob in der Gruppe eine .job-Datei liegt. Wenn das der Fall ist, werden alle Dateien der Gruppe kopiert. Es ist an dieser Stelle irrelevant, welche Endung die Dateien haben und es können auch mehr als zwei sein. 

    Und noch ein Tipp für Janniks Skript: Der -replace Operator verwendet RegularExpressions, die Replace-Methode jedoch nicht. 
    $Job.Replace(".job",".pdf")
    $Job -replace "\.job", "\.pdf"
    Der Vorteil ist, das der -replace Operator per Default nicht zwischen Groß/Kleinschreibung unterscheidet.

    Donnerstag, 6. April 2017 11:47
  • Hallo Jannik und Sir Hpot!

    Ich bin ganz glücklich mit Euren Lösungen und danke Euch sehr.

    Beide funktionieren super, und ich habe endlich einen Tritt in den Allerwertesten bekommen, mich mit solchen Scripten mal ernsthaft zu befassen.

    Ist aber auch toll, wie schnell man hier kompetente Hilfe bekommt.

    Der Hintergrund meiner Anfrage war, dass ich für ein Dokumentenmanagementsystem aus einer Quelle Dokumente ( die PDFs) und aus einer anderen Quelle Beschreibungsdateien ( die JOBs) bekomme, die nur zusammen in das System eingeführt werden können.

    Und um das zu automatisieren brauchte ich genau so eine Scriptlösung.

    Ihr seid Klasse! Danke danke!

    Jonathan der Boskop

    Donnerstag, 6. April 2017 12:57
  • ...Verbesserungsvorschlag: Filtern immer im Provider, wenn der das unterstützt, nicht in der Pipeline.
     
    >     $JobsToCopy = Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"} | select -ExpandProperty FullName
     
    Get-ChildItem -Path $( $Path + '\*.job' )
     
    Das wäre dann auch nicht mehr case sensitiv :)
     
    Oder so wie Christoph vorgeschlagen hat, gefällt mir auch sehr gut.
     
    Donnerstag, 6. April 2017 13:58
  • Hallo Jonathan,

    fand das eigentlich ganz interessant, daher hab ich mal was gebaut, was Dir helfen könnte.

    $Path = "T:\Location\Test\"
    $Destination4DoneJobs = "T:\Location\Ready\"
    
    $CountJobs = (Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"}).Count
    
    while ($CountJobs -gt 0) {
        $JobsToCopy = Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"} | select -ExpandProperty FullName
        
        foreach ($Job in $JobsToCopy) {
            $JobsPDF = ($Job.Replace(".job",".pdf"))
            if (Test-Path "$JobsPDF") {
                
                Move-Item "$Job" "$Destination4DoneJobs" -Force
                $JustWait = timeout 1 /nobreak
                if (Test-Path ($Job.Replace("$Path","$Destination4DoneJobs"))) {
                    Write-Host "Successfully copied $Job" -ForegroundColor Green
                    }
                Move-Item "$JobsPDF" "$Destination4DoneJobs" -Force
                if (Test-Path ($JobsPDF.Replace("$Path","$Destination4DoneJobs"))) {
                    Write-Host "Successfully copied $JobsPDF" -ForegroundColor Green
                    }
                }
            }
        $JustHide = timeout 5 /nobreak
        $CountJobs = (Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.job"}).Count
        
        }

    Das Script läuft solange mindestens eine Datei mit Endung ".job" im Ordner liegt. Danach beendet es sich nach ca. 5 Sekunden. Natürlich kannst Du die While-Schleife auch raus nehmen und das Script über die Aufgabenplanung alle X Minuten ausführen lassen.

    MfG

    Jannik D.

    Hallo Jannik,

    ich habe ja angedroht, mich nun weiter mit der Problematik beschäftigen zu wollen.

    Nachdem Eure Lösungen so schön funktionierten, habe ich mir erlaubt, für ein kleines Zusatzproblem Deinen Lösungsvorschlag zu modifizieren, und das hat auch funktioniert.

    Ausgangssituation:

    Im Quellordner tauchen auch JOB-Dateien auf, deren Dateinamen mit L ("Lieferscheine") oder  R ("Rechnungen") beginnt.

    Die zugehörigen PDF-Dateien gleichen Namens werden aber von den erzeugenden Prozessen nicht im Quellordner, sondern in einem dritten Ordner hinterlegt, und das kann auch nicht geändert werden.

    Das Script soll nun also solche Dateien erkennen und die zugehörigen PDFs aus dem Drittordner abholen, bevor dann wieder der Prozess des Verschiebens beginnt, der mit Eurem ersten Script gelöst wurde.

    Klar, für Euch lächerlich einfach, für mich erstmal nicht trivial.

    Die Modifikation deines Scripts sieht nun so aus:

    $Path = "Z:\Test\"
    $Destination4GetPDFs = "Z:\Test\"
    $Check4GetPDFs = "K:\Rechnung\"

    $CountJobs = (Get-ChildItem -Path $Path | Where-Object {$_.Name -like "[LR]*.JOB"}).Count

    $JobsToCopy = Get-ChildItem -Path $Path | Where-Object {$_.Name -like "[LR]*.JOB"} | select -ExpandProperty BaseName
        
    foreach ($Job in $JobsToCopy) {
        $PDFName = $Job + ".pdf"
            
        $CopyPDF = (Get-ChildItem $Check4GetPDFs | Where{$_.Name -like $PDFName}) | select -ExpandProperty FullName
            
        if (Test-Path "$CopyPDF") {
                
            Copy-Item "$CopyPDF" "$Destination4GetPDFs" -Force
            $JustWait = timeout 1 /nobreak
            if (Test-Path ($PDFName.Replace("$Path","$Destination4GetPDFs"))) {
                Write-Host "Successfully copied $PDFName" -ForegroundColor Green
                }
            }
        }
    $JustHide = timeout 5 /nobreak
    $CountJobs = (Get-ChildItem -Path $Path | Where-Object {$_.Name -like "*.JOB"}).Count
       

    Und das funktioniert auch.

    Meine Frage, warum ich das hier nochmal reinstelle ist ganz einfach:

    Ich bin mir sicher, dass ich mich da ziemlich naiv angestellt habe, und das man das auch sicher viel eleganter machen kann!

    Aber aller Anfang ist halt schwer, und mit 66 ist man eben nicht mehr so fix wie Ihr jungen Kerle!

    Ich wünsch Euch allen ein frohes Osterfest!

    Donnerstag, 13. April 2017 09:22