Powershell findet gewünschte Dateien

26. Januar 2010

Viele Anwender und Administratoren verbringen einen großen Teil ihrer Zeit vor dem Bildschirm damit, bestimmte Dateien auf den lokalen Festplatten ihrer Rechner zu suchen. In Zeiten, in denen die Größe solcher Platten in den Terabyte-Bereich geht, ist dies zumeist keine leichte Aufgabe. Bill Stewart zeigt in diesem Beitrag, wie der Einsatz eines Powershell-Skripts die Suche erleichtern kann. Dazu hat er Powershell-Skript geschrieben, dessen Fähigkeiten die des standardmäßigen „get-childitem“-Cmdlets der Powershell mit einer Reihe von Features erweitert. Das Ergebnis ist das Skript „Whereis.ps1“, das Abonnenten von NT4Admins direkt von unserer Seite im Kontext dieses Beitrags herunterladen können.

Systembetreuer und alle IT-Profis, die häufig direkt an der Kommandozeile ihres Windows-Systems arbeiten, finden es dabei sehr nützlich, auch die Suche nach bestimmten Dateien direkt von dort aus zu starten. Viele Administratoren werden dazu ebenso wie unser Autor das bekannte „dir“-Kommando einsetzen, das ihnen von cmd.exe direkt zur Verfügung gestellt wird.

Dabei kommen in der Regel wohl die beiden Parameter „/b“ und „/s“ zum Einsatz, da eine Kombination dieser beiden Schalter das „dir“-Kommando dazu veranlasst, eine Liste mit den vollständigen Pfaden und Dateinamen der Dateien anzeigen, die dem mitgegebenen Suchmuster entsprechen.
Allerdings bietet das Kommando dem Anwender keine einfache Syntax an, wenn es beispielsweise darum geht, Dateien an unterschiedlichen Orten im Dateisystem zu suchen.  So muss ein Administrator, der zum Beispiel alle Dateien auf den Laufwerken „C“ und „D“ finden möchte, deren Dateiendung „.doc“ lautet, den folgenden Aufruf verwenden:

dir /b /s c:\*.doc d:\*.doc

Sucht der Anwender dann noch nach Dateien mit unterschiedlichen Endungen, wie etwa alle „.doc“-, alle „.xls“- und alle „.ppt“-Dateien an verschiedenen Orten im Dateisystem, so werden Aufruf und Syntax noch weitaus komplexer: Schließlich muss er jeden zu durchsuchenden Ort und jedes Suchmuster separat eingeben.

Deutlich einfach wird diese Aufgabe unter Einsatz eines Cmdlets, das die Powershell bereit stellt. Es trägt die Bezeichnung „get-childitem“ und kann beispielweise mit dem folgenden Aufruf die Laufwerke „C“ und „D“ nach allen Dateien mit den Endungen „.doc“, „.xls“ und „.ppt“ durchsuchen:

get-childitem c:\*, d:\* -include *.doc, *.xls, *.ppt -recurse

Der erste Parameter, der dem Cmdlet übergeben wird, beinhaltet dabei eine Liste der Pfade, die zu durchsuchen sind. Mit Hilfe des Schalters „-include“ wird ebenfalls eine Liste spezifiziert, die „Wildcard“-Suchmuster (oder Platzhalterzeichen, wie sie in der deutschen Dokumentation von Microsoft genannt werden) enthalten, deren Aufgabe darin besteht den jeweils gesuchten Pfad zu qualifizieren. Analog zum Schalter „/s“ beim „dir“-Kommando veranlasst der „-recurse“-Parameter das Cmdlet dazu, alle Dateien und Unterverzeichnisse zu durchsuchen.

Ein Cmdlet reicht nicht: Das Skript „Whereis.ps1“ wird vorgestellt

Obwohl die Anweisung „get-childitem“ schon sehr mächtig in ihren Möglichkeiten ist, wollte der Autor doch ein Werkzeug auf der Kommandozeile zur Verfügung haben, das zusätzliche Funktionalitäten zu bieten hat. So bestand beispielsweise der Wunsch, den „-recurse“-Parameter auslassen zu können und dem Kommando beizubringen, dass es automatisch die vorhandenen lokalen Festplatten durchsucht, wenn der Anwender keinen Pfad beim Aufruf angibt.

So dauert es nicht lange und es entstand ein Powershell-Skript, dessen Fähigkeiten die des „get-childitem“-Cmdlets mit einer Reihe von Features erweitert. Das Ergebnis ist das Skript „Whereis.ps1“. Ein Hinweis ist an dieser Stelle noch für all die Systemprofis, die sich schon eingehender mit den verschiedenen Ausprägungen der Unix- und Linux-Systeme befasst haben: Auch wenn der Name sehr ähnlich klingt, stellt dieses Skript kein Äquivalent zu dem auf diesen Betriebssystemen zu findenden Kommando „whereis“ dar!

Das Powershell-Skript „whereis.ps1“ verwendet die folgende Syntax:

Whereis.ps1 –Name <String []>
[-Path <String >]
[-LastWriteTimeRange <DateTime[]>]
[-SizeRange <UInt64[]>] [-OneLevel]
[-Files] [-Dirs] [-Force] [-DefaultFormat]

Der Parameter „-Name“ legt dabei das Suchmuster fest, wobei sein Argument auch in Form eines Datenbereichs (Array) mitgegeben werden kann. Alle Dateien und Verzeichnisse, die dem übergebenen Suchmuster entsprechen, werden dem Output des Skripts hinzugefügt. Beim Aufruf muss der Anwender zwingend nur einen einzigen Parameter angeben und das ist „-Name“.

Wer mehr Informationen zu den Platzhalterzeichen braucht und wissen welche, welche Suchmuster er hier anwenden kann, sollte die internen Hilfeseiten der PowerShell mit folgenden Aufruf befragen:

get-help about_wildcard

Direkt am Shell-Prompt eingegeben, zeigt die Powershell umfangreiche Informationen zu diesem Thema an (wie es auch der Screenshot in Bild 1 zeigt).
Der Parameter „-Name“ wird bei diesem Skript durch seine Position bestimmt, so dass der Anwender den Namen dieses Parameters weglassen kann, und einfach das Suchmuster direkt an erster Stelle hinter dem Skriptnamen angeben kann.

Der Parameter „-path“ spezifiziert den Suchpfad und kann als Argument auch ein Array übernehmen. Lässt der Anwender diesen Parameter beim Aufruf weg, so durchsucht das Skript automatisch alle lokalen Festplatten des Systems. Auch dieser Schalter wird durch seine Position festgelegt, der Nutzer kann also diese Bezeichnung ebenfalls weglassen, indem er einfach die entsprechenden Argumente beim Aufruf an zweiter Stelle hinter dem Skriptnamen angibt.

Der nächste mögliche Parameter „-LastWriteTimeRange“ spezifiziert einen Datenbereich, der bei der Ausgabe mit eingeschlossen werden soll und kann ebenfalls als Array angegeben werden. Alle „Treffer“, deren „LastWriteTime“-Eigenschaft sich innerhalb des angegebenen Bereichs befindet, werden der Ausgabe des Skripts hinzugefügt. Gibt der Anwender für diesen Parameter eine Zeichenkette an, so versucht das Skript diese in ein entsprechendes „DateTime“-Objekt zu konvertieren.

Fügt er hingegen nur ein einzelnes Datum ein, so wird dies von „Whereis.ps1“ in der Form „dieser Datum oder später“ interpretiert. Übergibt der Anwender dem Skript einen Datenbereich, so interpretiert es bei dieser Angabe dessen erstes Element als das frühere Datum und das letzte entsprechend als die spätere Grenze des zu suchenden Bereichs. Wer eine Suche des Typs „älter als…“ starten möchte, kann dies tun, indem als erstes Element des Arrays eine „0“ angibt.

In der Tabelle 1 sind einige Beispiele für Einsatz dieses Parameters aufgelistet.

Beispiele für den „-LastWriteTimeRange“-Parameter
Bereich                                                      Erläuterung
2009/01/01                                                Daten die am 01. Januar 2009 oder
                                                                  später verändert wurden
“Jan 1, 2007“, “Dec 31, 2007 23:59:59“    Daten, die irgendwann im Jahr 2007
                                                                  verändert wurden
0, “30 Jun 2008 11:59:59 pm“                   Daten, die am 30. Juni 2008 oder
                                                                   früher verändert wurden
0,((get-date) –(new-timespan –days 30))  Daten, die vor 30 Tagen oder früher
                                                                   verändert wurden
((get-date) –(new-timespan –days 30)), (get-date)  
                                                                  Daten, die während der letzten
                                                                  30 Tage verändert wurden.

Der „-SizeRange“-Schalter legt analog zum vorherigen Parameter einen Größenbereich in Byte für die Dateien fest, die in der Ausgabe mit angegeben werden sollen. Auch hier kann natürlich sowohl ein einzelner Wert als auch ein Datenbereich angegeben werden. Gibt der Nutzer einen einzelnen Wert an, so wird das Skript dies als „Datei, die mindestens diese Größe besitzt“ interpretieren und bearbeiten. Kommt ein Datenbereich zum Einsatz, so stellt wiederum der erste Wert die untere Grenze des Suchbereichs dar, während der zweite diesen nach oben abgrenzt.

Bei dem Argumenten steht dem Anwender zusätzlich die Möglichkeit zur Verfügung, die entsprechenden nummerischen Endungen der Powershell wie „kb“, „mb“ und „gb“ zu verwenden, um die Größen zu spezifizieren.

Beispiele für den „-SizeRange“-Parameter

Bereich                      Erläuterung
32kb                           Daten, die 32 KByte oder größer sind
0,32kb-1                     Daten, die kleiner als 32 KByte sind
05gb                           Daten, die 5 GByte oder kleiner sind

Die Tabelle 2 listet auch zum Einsatz dieses Parameters einige Beispiele auf. Dieser Paramater wird vom Skript ignoriert, wenn der Parameter „-Dirs“ (wird noch detaillierter in Lauf des Artikels vorgestellt) ebenfalls zum Einsatz kommt, da Verzeichnisse keine „Length“-Eigenschaft besitzen.

Zu den weiteren Schaltern beziehungsweise Parametern, die das Verhalten des Skripts steuern, gehört auch „-OneLevel“, der das genau Gegenteil zu dem zu Beginn vorgestellten Parameter „-Recurse“ bei Cmdlet „GetChildItem“ darstellt: Durch seinen Einsatz wird nur genau dieses eine Verzeichnis ohne Unterverzeichnisse durchsucht.

Der Parameter „-Files“, der auch die Default-Einstellung des Skriptes darstellt, veranlasst das Programm nur Dateien in der Ausgabe anzuzeigen, während „-Dirs“ nur Verzeichnisse auf den Schirm bringt. Die Kombination beider Parameter sorgt dann dafür, dass sowohl Dateien als auch Verzeichnisse angezeigt werden.

Wie schon bei dem „GetChildItem“-Cmdlet bewirkt auch bei diesem Skript die Verwendung des Parameters „-force“, dass auch die Dateien angezeigt werden, die solche Attribute wie „Versteckt“ oder „System“ besitzen. Schließlich steht dem Anwender noch der „-DefaultFormat“-Parameter als zusätzlicher Schalter zur Verfügung: Standardmäßig gibt das Skript die gefundenen Dateinamen als formatierte Zeichenketten aus, was die Lesbarkeit auf dem Schirm gerade dann erhöht, wenn ein Verzeichnis sehr viele Treffer zu bieten hat.

Bild 2. Der „-DefaultFormat“-Parameter als zusätzlicher Schalter im Einsatz: Standardmäßig gibt das Skript die gefundenen Dateinamen als formatierte Zeichenketten aus, was die Lesbarkeit auf dem Schirm erhöht. Werden als Ausgabe aber Objekte vom Typ „File-System“ erwartet, so kann das mit diesem Schalter erreicht werden.

Wird das Skript aber beispielsweise dazu eingesetzt, die Ausgabe an ein anderes Cmdlet weiterzugeben, das als Eingabe Objekte vom Typ „File-System“ erwartet, so kann das mit Hilfe dieses Parameters erreicht werden. Bild 2 zeigt diese beiden unterschiedlichen Arten der Ausgabe.

Suchen und Finden im Detail: Wie das Skript arbeitet

Am Beginn des Skripts ist ein „param“-Statement zu finden, mit dessen Hilfe die Kommandozeilen-Parameter des Programms definiert werden. Typischerweise setzt unser Autor in seinen PowerShell-Skripten für die Skriptparameter und andere globale Variablen Namen ein, die eine gemischte Groß- und Kleinschreibung (mix-case) verwenden. Das ist allerdings nur eine von ihm eingehaltene Konvention und muss so nicht befolgt werden.

Nach dem „param“-Statement deklariert das Skript dann drei Funktionen: „usage“, „isnumeric“, „writeItem“ und natürlich die Main-Funktion des Programms. Danach ruft das Skript die Funktion „main“ auf. In diesem Zusammenhang ist noch einmal der Hinweis wichtig und angebracht, dass in der PowerShell alle Funktionen zunächst definiert sein müssen, bevor sie aufgerufen werden können. Aus diesem Grund ruft das Skript „Whereis.ps1“ die Main-Funktion auch erst in der allerletzten Zeile auf.

Die „usage“-Funktion gibt auf der Konsole eine Nachricht aus, in der das Skript und die Art wie es einzusetzen ist, erläutert werden. Die Main-Funktion ruft diese Funktion dann auf, wenn das Skript ganz ohne einen Parameter oder mit dem Parameter „-Help“ von der Kommandozeile gestartet wird.
Die Hauptfunktion „Main“ ruft die Funktion „isnumeric“ auf, um auf diese Weise sicherzustellen, dass es sich bei den Argumenten, die dem Parameter „-SizeRange“ mitgegeben wurden, auf wirklich um nummerische Werte handelt. Dazu setzt die Funktion den Operator „-contains“ ein. Sie stellt damit fest, ob der Typ des Parameters sich in der Liste der nummerischen Werte wie „Decimal“ oder „Double“ befindet.

Die Funktion „writeItem“ dient dazu, das Format der Ausgabe zu kontrollieren. Hat der Anwender an der Kommandozeile den Parameter „-DefaultFormat“ mit angegeben, so gibt die Funktion ganz einfach ihr Argument aus, bei dem es sich um ein Objekt vom Typ „File-System“ handelt. Ansonsten dient die Funktion dazu, eine formatierte Zeichenkette auszugeben. Dazu setzt sie die Standardformatierungs-Codes von .NET-String ein und den „-f“-Operator ein. Nähere Informationen zu diesen Formatierungen stellt ein Artikel auf Microsoft MSDN-Seiten zur Verfügung, der aus dem .NET-Entwicklerhandbuch stammt und den Titel „Formatierung von Typen“ trägt.

Der Kernstück des Skripts: Die Main-Funktion

Die Main-Funktion beinhaltet natürlich den Großteil des Source-Codes dieses Skripts. Die erste Aufgabe dieser Funktion besteht nun darin sicherzustellen, dass der „-Name“-Parameter beim Aufruf mit angegeben wurde. Fehlt er oder findet die Funktion den Parameter „-Help“, dann ruft sie die Funktion „usage“ auf, die ihre Mitteilung ausgibt und das Skript beendet.

Danach wird die $Name-Variable in ein Array konvertiert, wobei sie natürlich unverändert bleibt, wenn sie bereits einen solchen Datenbereich enthält. Mittels einer Schleife wird dieser Bereich dann abgearbeitet. Wenn ein Element dieses Bereichs den Platzhalter „*“ enthält, so wird der Datenbereich durch den $NULL-Wert ersetzt. Dieser Schritt ist notwendig, um das später im Skript zum Einsatz kommende Cmdlet „Get-ChildItem“ davon abzuhalten, Unterverzeichnisse auszugeben, die sich unterhalt eines Verzeichnisses befinden.

Im nächsten Schritt überprüft unsere Hauptfunktion, ob nun der Parmeter „-Path“ vorhanden ist. Wurde er nicht mit angegeben, so ruft die Funktion das Cmdlet „Get-WmiObject“ auf, um mit seiner Hilfe die Liste der lokalen Festplatten abzuarbeiten. Der folgende Ausschnitt aus dem Skript-Code zeigt diesen Programmschritt:

# If no -path parameter, use WMI to collect a list of fixed drives.
  if (-not $Path) {
    $Path = get-wmiobject Win32_LogicalDisk -filter DriveType=3 | foreach-object {
      $_.DeviceID
    }
  }

  # Convert $Path into an array so we can iterate it.
  $Path = @($Path)

Danach wird die Variable $Path also eine von zwei Varianten beinhalten: Entweder sind dort einfach der Pfad oder die Pfade zu finden, die über den Parameter „-Path“ angegeben wurden, oder an dieser Stelle befindet sich eine Liste mit den lokalen, fest installierten Festplatten. Danach konvertiert die Hauptfunktion diese Variable in ein Array (siehe Listing-Ausschnitt oben), was auch hier wieder automatisch entfällt, wenn es sich hier bereits um einen derartigen Bereich handelt.

Anschließend – und aus diesem Grund muss sich in der $Path-Variablen auch ein Array befinden, wird dieser Datenbereich mittels einer Schleife abgearbeitet, was der nächste Listing-Ausschnitt sehr schön verdeutlicht:

# If a path ends with "\", append "*". Then, if it doesn’t end with
# "\*", append "\*" so each path in the array ends with "\*".

  for ($i = 0; $i -lt $Path.Length; $i++) {
    if ($Path[$i].EndsWith("\")) {
      $Path[$i] += "*"
    }
    if (-not $Path[$i].EndsWith("\*")) {
      $Path[$i] += "\*"
    }
}

Dabei überprüft das Programm für jedes Element des Arrays, ob es mit einem Backslash (\) endet. Wenn dem so ist, fügt die Funktion den Platzhalter „*“ dem Pfad hinzu. Danach überprüft sie, ob das Element mit der Zeichenfolge „\*“ endet. Ist dem nicht so, hängt die Funktion diese Zeichenkette an. Deshalb wird jedes Element des Arrays nach dem kompletten Durchlauf der Schleife mit der Zeichenkette „\*“ enden.

Durch diesen Vorgang ist es möglich, dass der Anwender Pfade in der Form „C:\Dateien“ angibt und das Skript dies dann als „C:\Dateien\*“ interpretiert und anwendet. Dieser Schritt erspart dem Anwender nicht nur Tipparbeit, sondern ist notwendig, weil die Main-Funktion den Parameter „-Include“ des „GetChildItem“-Cmdlets einsetzt. Dieser Parameter dient dem Cmdlet als Auswahlmuster für den „-Path“-Parameter:

So werden von „GetChildItem“ nur Werte aus dem spezifiziertem Pfad zurückgegeben, die dem Argumenten des „-Include“-Parameters entsprechen. Damit „-include“ aber korrekt arbeiten kann, muss der Programmierer ein Platzhaltermuster (Wildcard) oder einen Dateinamen für das Argument des „-Path“-Parameters mit übergeben.

Diese zunächst kompliziert wirkende Bedingung wird durch die folgenden Beispiele sicher besser verständlich. So würde der folgende Aufruf keine Resultate anzeigen, selbst wenn das Verzeichnis „C:\Daten“ existiert und Dateien mit der Endung „.txt“ beinhaltet:

get-childitem c:\Daten -include *.txt

Das Kommando arbeitet so, weil kein Platzhaltermuster für „-include“ existiert, um diesen Aufruf zu qualifizieren: Es handelt sich hier dann also ganz einfach nur um einen Verzeichnisnamen, dem aber keinerlei Suchmuster für eine Datei in diesem Verzeichnis mitgegeben wurde. Also scheint es nur logisch, den folgenden Aufruf zu starten:

get-childitem c:\Daten\* -include *.txt

was dann zu dem gewünschten Ergebnis führt, dass die Dateien mit der Endung „.txt“ im Verzeichnis „C:\Daten“ angezeigt werden.
Soll dieses Verwalten auch auf die im Startverzeichnis vorhandenen Unterverzeichnisse ausgedehnt werden, so kann der Platzhalter „*“ direkt mit dem „-include“-Parameter verwendet werden:

get-childitem c:\Daten\* -include *

Der Platzhalter „*“ trifft auch für Verzeichnisse zu, so dass der letzet Aufruf nicht nur alle Dateien im Verzeichnis „C:\Daten“ sondern auch alle darin vorhandenen Unterverzeichnisse (nur auf dem ersten Level, der Aufruf ist nicht rekursiv) und die darin enthaltenen Dateien auf. Will man nur die Dateien im „C:\Daten“ anzeigen, so kann einfach der Parameter „-Include“ entfallen oder aber mit dem Argument „$NULL“ übergeben werden.

Nach diesem kurzen Ausflug geht es zurück zur Hauptfunktion, die im nächsten Schritt überprüft, ob der Parameter „-Last WriteTimeRange“ existiert. Tut er das nicht, so legt das Skript ein Array an, das aus zwei Elementen besteht. Darin speichert die Funktion das frühestmögliche Datum (das ist der 01. Januar 0001, 00:00:00) im ersten und das letztmögliche Datum (31. Dezember 9999, 23:59:59) ab. Diese Werte bekommt die Funktion indem sie die statischen Eigenschaften „MinValue“ und „MaxValue“ des „DateTime“-Typs abfragt.

Wurde der „-Last WriteTimeRange“-Parameter hingegen beim Aufruf angegeben, so wird auch hier wieder die Variable in ein Array konvertiert, so es sich bei ihr nicht schon um einen solchen Datenbereich handelt. Ist es ein Array, das nur ein Element enthält, so hängt die Funktion diesem ein zweites Element mit dem letztmöglichen Datum automatisch an.

Entsprechend verhält sich die Funktion, wenn das erste Element des eingegeben Datenbereich eine „0“ enthält, an diese Stelle wird dann das frühestmögliche Datum eingesetzt. Danach versucht die Funktion, beide Elemente in Objekte des Typs „DateTime“ umzuwandeln, was der folgende Listing-Abschnitt demonstriert:

# Throw an error if [DateTime]::Parse() fails.
    trap [System.Management.Automation.MethodException] {
      throw "Error parsing date range. String not recognized as a valid DateTime."
    }
    # Parse the first two array elements as DateTimes.
    for ($i = 0; $i -lt 2; $i++) {
      $LastWriteTimeRange[$i] = [DateTime]::Parse($LastWriteTimeRange[$i])
    }

Dazu kommt die statische Methode „Parse“ des Objekts „DateTime“ zum Einsatz. Sollte diese Methode allerdings einen Fehler zurückgeben, wird der Skript-Block hinter dem „trap“-Statement ausgeführt, der eine entsprechende Fehlermeldung ausgibt und das Skript anhält. Die Funktion versucht dann zu verifizieren, ob das erste Datum früher ist als das zweite angegebene Datum:

Sollte das nicht zutreffen, gibt sie einen Fehler aus und beendet das Skript.
Dann ist der Parameter „-SizeRange“ an der Reihe: Auch geht die Funktion wiederum so vor, dass sie automatisch ein Array mit zwei Elementen anlegt, wenn dieser Parameter nicht angegeben wurde. Das erste Element bekommt dabei den Wert „0“ zugewiesen, während das zweite den maximalen Wert für ein 64-Bit-Integer ohne Vorzeichen (UInt64) beinhalten wird. Wurde der Parameter mitgegeben und handelt es ich bei ihm nicht um ein Array, so wird die Variable auch hier entsprechend zu einem Datenbereich umgewandelt.

Beinhaltet das Array nur einen Wert, so wird der zweite mit dem Maximalwert des UInt64-Typs gefüllt. Der nächste Listing-Ausschnitt zeigt, wie die Funktion dann mittels der „isnumeric“-Funktion feststellt, ob es sich bei den Daten tatsächlich um nummerische Werte handelt:

# Ensure the elements in the size range are numeric.
  for ($i = 0; $i -lt 2; $i++) {
    if (-not (isNumeric $SizeRange[$i])) {
      throw "Size range must contain numeric value(s)."
    }
  }

Beinhaltet eines der Elemente dabei einen nicht-nummerischen Wert gibt die Funktion eine Fehlermeldung aus und beendet das Skript. Ein Fehler wird ebenfalls dann angezeigt, wenn das erste Element größer ist als das zweite.

Nun überprüft die Funktion, ob die Parameter „-Files“ und „-Dirs“ nicht an der Kommandozeile mit eingegeben wurden. Wenn keiner der beiden Parameter existiert, setzt die Funktion die Variable „$Files“ auf den Wert TRUE. Danach werden noch Zähler auf den Wert „0“ gesetzt: Einer, der mitzählt, wie viele Daten gefunden wurden ($count) und einer der die Größe aller Dateien zusammenrechnet ($sizes).

Hier ist endlich der Punkt erreicht, an dem die Main-Funktion alle Parameter abgearbeitet und überprüft hat. Nun kann also das „Get-ChildItem“-Cmdlet ausgeführt werden. Die Funktion leitet dann die Ausgabe dieses Cmdlets mittels einer Pipe an „ForEach-Object“ weiter, das dann für jedes Objekt weitere Filterungen durchführen kann. Wurde der Parameter „-Files“ angegeben und die Eigenschaft „PsIsContainer“ besitzt den Wert „False“ (was bedeutet, dass es sich bei diesem Objekt nicht um eine Verzeichnis sondern um eine Datei handelt), so geht die Funktion darin zu überprüfen, ob die Eigenschaften „LastWriteTime“ und „Length“ sich im vorgegebenen Bereich befinden.

Trifft das zu, so setzt die Funktion die beiden Zähler in den Variablen „$count“ und „$sizes“ entsprechend hoch und reicht das Objekt an die „writeItem“-Funktion weiter, die es dann ausgeben kann. Ganz ähnliche Schritte führt die Funktion aus, wenn die Eigenschaft „PsIContainer“ den Wert „True“ besitzt, es sich bei dem Objekt also um ein Verzeichnis handelt. Allerdings wird die Funktion dann weder den Größenbereich überprüfen noch die „$size“-Variable heraufsetzen, weil Verzeichnisobjekte keinen Eigenschaft „Length“ besitzen.

Das Skript in der Praxis: beispielhafte Aufrufe

Abschließend wollen wir noch ein paar beispielhafte Aufrufe des Skripts zeigen, die es im Einsatz für die verschiedensten Aufgaben zeigen. Wer beispielsweise Video- und Audiodateien auf den lokalen Festplatten finden möchten, kann dazu den folgenden Aufruf einsetzen:

whereis.ps1 *.asf,*.avi,*.mov,*.mp3,
     *.mp4,*.mpg,*.mpeg,*.qt,*.wav,*.wm,*.wmv

Ein wichtiger Hinweis zu der Art und Weise, wie diese Aufrufe hier auf der Webseite dargestellt werden: Aus Platzgründen findet hier zumeist ein Umbruch statt, an der Kommandozeile sind diese Aufrufe aber in einer Zeile einzugeben. Der folgende Suchaufruf dient dazu, alle PowerPoint-Dateien, die größer als 10 MByte sind und sich im Verzeichnis C:\Data oder darunter befinden:

whereis.ps1 *ppt[st] *
   C:\Data –sizerange 10mb

Ein etwas komplexerer Aufruf zeigt, wie alle Dateien im Verzeichnis C:\Logs gelöscht werden können, die vor 30 Tagen oder früher verändert wurden:

whereis.ps1 * c:\Logs
    -daterange 0,((get-date)
   – (new-timespan -days 30))
   -onelevel -defaultformat | remove-item

Bereits das Standard-Cmdlet der Powershell bietet eine Reihe von mächtigen Funktionen, um Dateien und Verzeichnisse nach bestimmten variablen Suchmustern zu finden. Das von uns hier vorgestellte und zum Download bereitstehende Skript „Whereis.ps1“ erweitert diese Möglichkeiten enorm und stellt zudem eine gute Anleitung für die eigenen Skripts dar, die sich mit der Suche und Bearbeitung von Dateien und Verzeichnissen im Dateisystem befassen.

Bill Stewart/fms

Lesen Sie auch