Naja, wie so oft... Kommt Es Darauf An (tm).
@DPXone hat ja schon ein paar Dinge erwähnt.
Aber dennoch ist die Pipeline nicht inhärent schlecht. Ganz im Gegenteil. Die Pipeline ermöglicht sogenanntes lazy loading/late binding. Heißt: Ausgaben werden gefiltert. Wenn ich vorne 1'000 Elemente reinstecke und nach dem nächsten Befehl in der Pipeline nur noch 50 Elemente übrigbleiben, dann werden die nachfolgenden Befehle nicht mehr 1'000fach, sondern (maximal) 50fach ausgeführt.
Als nächstes muß man bei PowerShell im Sinne der Performance immer im Hinterkopf behalten, daß hier seit Version 3 mit Listen gearbeitet wird (unter Win7 heißt das: WMF 5.1 nachinstallieren). Schleifen sind weder erforderlich noch besonders performant.
Beispiel:
PowerShell:
Get-ChildItem -Path abc -File -Recurse| Remove-Item
ist ein typisches PowerShell-Scriptfragment (strukturell).
- Dateien aus dem Pfad abc werden rekursiv gesucht (nur Dateien, keine Ordner).
- Sobald eine gefunden wird (!) wird dieser Fund in die Pipeline geschoben.
- Dort wartet Remove-Item. Ergebnis: noch während der Suche werden die Funde gelöscht. Dateien suchen und löschen ist (pseudo)parallel.
Wie man auch erkennen kann, wird an keiner Stelle berücksichtigt, was Get-Childitem tatsächlich zur Laufzeit findet oder nicht. Eine Datei => funktioniert. Zwei oder mehr => funktioniert. Gar keine Datei => es wird natürlich nichts gelöscht, klar, aber dieser Fall muß ebenfalls nicht berücksichtigt werden.
Man könnte stattdessen auch den Befehl auftrennen:
PowerShell:
[System.IO.FileInfo[]] $files = Get-Childitem -Path abc -File -Recurse
$files | Remove-Item
$files | % {Remove-Item -Path $_.FullName}
foreach($file in $files)
{
$file | remove-item
Remove-Item -Path $file.FullName
}
Semantisch passiert hier dasselbe. Man führt den Code aus und hinterher gibt es in abc keine Dateien mehr, wohl aber noch die Unterordner.
Der
Unterschied liegt im Detail.
- zuerst werden alle Dateien gesucht. Das belegt Platz und dauert Zeit.
- wenn alle Dateien gefunden wurden, werden sie (A) am Stück oder (B, C) per weiterer Schleife gelöscht.
- Suchen und löschen ist nun seriell.
- B und C unterscheiden sich dadurch, daß B eine Menge zur Einzelverarbeitung an die Pipeline übergibt und C ein "höherliegendes" Sprachkonstrukt ist. An dieser Stelle ist, wie schon erwähnt, performanter. C1 und C2 sind dagegen völlig austauschbar, auch in bezug auf die Performance, weil hier immer nur ein einziges Dateiobjekt übergeben wird. Formal verarbeitet C1 eine Liste mit einem Element und C2 ein einzelnes Element.
- Pro, man kann während des Suchens noch abbrechen, ohne daß auch nur eine Datei entfernt wurde.
- Contra, ich brauch mehr Ressourcen, ich muß mehr Code schreiben und es dauert insgesamt länger.
Man muß also immer schauen, was man genau tut und wie man es angeht. In den meisten Fällen braucht man in PS keine Schleifen - das geht soweit, daß man im Beispiel oben einfach $files.Fullname sagen kann, OBWOHL $files ein Array ist und .Fullname eine Eigenschaft eines einzelnen Objekts -- "normal" geht sowas nicht und soll auch gar nicht gehen.
Wenn man eine Schleife braucht, weil es anders nicht geht: wieder überdenken, was man erreichen möchte. Eine Schleife von..bis... bauen und drin dann jede Menge if then else oder switch($x)? Dann macht man was... nicht falsch, aber suboptimal, weil man mit Pipeline und Where-Object (Alias "?") signifikant performanter und vor allem auch leserlicher dasteht.
Das letzte Bißchen Performance kitzelt man entsprechend aus PS raus, wenn man auf Schleifen ganz verzichtet und überall dort, wo man normalerweise eine verwenden
müßte, ein cmdlet schreibt (in einer der NET-Sprachen, sprich kompiliert), welches die Liste der sonst an die Schleife übergebenen Einzelobjekte als Liste annehmen und verarbeiten kann und hinten die fertig verarbeitete Liste gemäß Anforderungen wieder ausspuckt.
Darüber hinaus ist in den meisten Fällen das binäre cmdlet performanter als die Scriptdatei. Andererseits ist das cmdlet natürlich unveränderlich. Entsprechend müßte man an der Stelle ein bißchen planen und natürlich auch fragen, ob es den Extraaufwand wert ist.
Generell: je mehr man zu verarbeiten hat, desto eher sollte man den "Script"-Ansatz zurückstellen und auf binäre Verarbeitung setzen. Dann macht man nur noch ein kleines Script drumrum, was die binär kompilierten Funktionen aufruft.