Benutzer mit den meisten Antworten
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
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 FullNameGet-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.
- Als Antwort markiert Jonathan Boskop Montag, 10. April 2017 07:06
-
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.
- Als Antwort vorgeschlagen Denniver ReiningMVP, Moderator Freitag, 7. April 2017 08:22
- Als Antwort markiert Jonathan Boskop Montag, 10. April 2017 07:05
-
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.
- Bearbeitet hpotsirhc Donnerstag, 6. April 2017 11:50
- Als Antwort vorgeschlagen Denniver ReiningMVP, Moderator Freitag, 7. April 2017 08:22
- Als Antwort markiert Jonathan Boskop Montag, 10. April 2017 07:06
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
- Als Antwort vorgeschlagen Denniver ReiningMVP, Moderator Freitag, 7. April 2017 08:21
- Nicht als Antwort vorgeschlagen Denniver ReiningMVP, Moderator Freitag, 7. April 2017 08:22
-
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.
- Als Antwort vorgeschlagen Denniver ReiningMVP, Moderator Freitag, 7. April 2017 08:22
- Als Antwort markiert Jonathan Boskop Montag, 10. April 2017 07:05
-
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.MoveItemCommandund 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
-
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.
-
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.
- Bearbeitet hpotsirhc Donnerstag, 6. April 2017 11:50
- Als Antwort vorgeschlagen Denniver ReiningMVP, Moderator Freitag, 7. April 2017 08:22
- Als Antwort markiert Jonathan Boskop Montag, 10. April 2017 07:06
-
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
-
...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 FullNameGet-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.
- Als Antwort markiert Jonathan Boskop Montag, 10. April 2017 07:06
-
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!