FĂŒr alle, die aus ihrem "Extra-Tool" keine Fortschrittsleiste rauskriegen:
- PowerShell kennt ein cmdlet Write-Progress. Damit kann man auf den Progress-Ausgabestrom schreiben und erhÀlt eine Progressbar am oberen Fensterrand (des Konsolenfensters).
- Write-Progress ist aber ein simples cmdlet. Man muĂ es aufrufen, und damit es etwas Erkennbares tut, muĂ es in einer Schleife ausgefĂŒhrt werden (egal wie die beschaffen ist). Hier gibt es jetzt zwei grundlegende AnsĂ€tze, mit denen man seine Progressbar ansteuern kann.
A
Wir haben eine Serie von (spĂ€testens zur Laufzeit bestimmbar) vielen einzelnen Aktionen, deren AusfĂŒhrungsstand wir mit Write-Progress dokumentieren wollen. Das ist der
synchrone Ansatz.
Beispiel: Alle Dateien aus einer Ordnerstruktur sollen gelöscht werden.
Implementierung (grundlegend; das Beispiel ist konstruiert):
PowerShell:
$Files = Get-Childitem -Recurse -Path C:\
1 .. $Files.Count |
% {
[int] $index = $_
Write-Progress -PercentComplete ( $index * 100 / Files.Count ) ....
$Files[$index] | Remove-Item
}
Vorteil: Der Ansatz ist ziemlich einfach und funktioniert fast ĂŒberall,
wenn die Vorbedingungen stimmen und ich
Zugriff auf die Schleife hab.
Nachteil: Ich brauche Zugriff auf die Schleife. Einen einzelnen lange dauernden ProzeĂ kann ich so nicht visualisieren.
B
FĂŒr einzelne, lange dauernde Prozesse brauche ich einen
asynchronen Ansatz. Beispiel: Ich will eine Progressbar haben fĂŒr die Position in einer Mediendatei.
Hier muĂ ich mit Threads arbeiten oder eine der Möglichkeiten von PS nutzen, eine Aufgabe im Hintergrund ausfĂŒhren zu können. Das geht unter anderem mit den *-Job cmdlets.
Dieser Ansatz ist individueller. Er funktioniert fast immer, solange ich nur in der Lage bin, Fortschrittsinformationen aus dem laufenden Task abzuleiten oder selbst zu generieren. Hierbei ist aber darauf zu achten, daĂ der Abgleich nicht aufwendiger wird als der eigentliche Task, denn sonst warte ich auf die Synchronisation und nicht auf die Erledigung meiner Aufgabe.
Der Nachteil ist, daĂ es kaum pauschale Vorgehensweisen gibt. Man muĂ fĂŒr jedes Problem einen eigenen Abgleich finden, je nachdem, was mir die Implementierung meines Tasks ermöglicht und was nicht.
Beispiel 1
Ich hab die (fiktiven) Methoden zur TaskausfĂŒhrung
++ object Execute()
++ Task BeginExecute() und
++ object EndExecute(Task) zur VerfĂŒgung.
Execute() ist die synchrone Variante - die Aktion wird ausgefĂŒhrt und ich warte aufs Ergebnis. Es gibt einen RĂŒckgabewert (das Ergebnis). Das ist das normale Vorgehen.
BeginExecute() ist die asynchrone Variante. Die liefert ein Taskobjekt, welches mir sagt, was der ProzeĂ grad macht.
EndExecute(Task) schlieĂt den Task ab.
FĂŒr den Fortschritt kann ich jetzt
- BeginExecute ausfĂŒhren. Die Aufgabe wird gestartet und im Hintergrund ausgefĂŒhrt.
- Write-Progress ausfĂŒhren auf Basis der Information aus dem Task-Objekt. Write-Progress lĂ€uft nun parallel zur Aufgabe.
Problem: Wenn das Taskobjekt mir keine Infos liefert zum Umfang der Aufgabe, dann muĂ ich das vorher bestimmen und das kostet wieder Zeit; Zeit, in der die Aufgabe schon hĂ€tte ausgefĂŒhrt werden können.
Beispiel 2
ich hab solche Statusinformationen
nicht zur VerfĂŒgung. Dann muĂ ich den Status zur Laufzeit selber ermitteln und mich fragen, wie ich das am besten anstelle, ohne daĂ mir das zeitlich aus dem Ruder lĂ€uft.
Also fange ich einfach mit der Aufgabe an und laĂ die im Hintergrund laufen.
Und jetzt hole ich mir den Fortschritt selber zur Laufzeit. Was wurde schon verarbeitet? Was nicht? Das ist mein Fortschritt und den muĂ ich mit Write-Progress visualisieren.
Dabei muĂ ich aber aufpassen, daĂ ich keinen Deadlock verursache, weil mein Ăberwachungstask mir Dinge blockiert, die der AusfĂŒhrungstask selber blockieren muĂ.
FĂŒr diese Situation ist es daher im Gegensatz zum synchronen Ansatz vorteilhaft, sich seine UpdatehĂ€ufigkeit fĂŒr den Fortschrittsbalken gut zu ĂŒberlegen. Der synchrone Ansatz aktualisiert mit erledigter Teilaufgabe. Die haben wir hier aber nicht. Stattdessen mĂŒssen wir uns das Teilergebnis periodisch selber ermitteln und wenn wir das einmal pro Millisekunde tun wĂŒrden, haben wir wenig Informationsgewinn und viel Overhead; wenn wir aber zu selten prĂŒfen, ist die Aufgabe fertig, ohne daĂ wir den Fortschrittsbalken zu Gesicht bekommen.
C
Der Multithreaded-Ansatz. Der ist gleichzeitig "einfach" und "der schwerste". Ich muĂ mein Problem in Teilaufgaben zerlegen und versorge Write-Progress mit der Anzahl der erledigten Teilaufgaben.
Beispiel: Ich will einen Ordner samt Unterordnern löschen.
Also erstelle ich einen Task fĂŒr jeden unmittelbaren Unterordner und lösche die jeweils rekursiv. Als Ordnerstruktur komm ich nicht in die Verlegenheit, daĂ sich die einzelnen Tasks gegenseitig stören.
Meine Ausgabe fĂŒr Write-Progress wird jetzt den Verlauf abschĂ€tzen, indem nicht die Anzahl der Dateien, sondern die jeweils erfolgreich gelöschten Unterordner protokolliert werden; das hĂ€ngt dann davon ab, wieviele Einzeltasks ich erstellen konnte. Nur einer und ich hab gar nichts gewonnen.