Daten|teiler
Kopieren als Kulturtechnik

Suchen und auflisten in der PowerShell

14. Juli 2011 von Christian Imhorst

In der Windows-Befehlszeile cmd.exe oder in DOS hat man zum Auflisten von Dateien den DIR-Befehl benutzt. Der Befehl klappt in der PowerShell auch noch, ist allerdings ein Alias zum Cmdlet Get-Childitem.
Wenn man den Befehl DIR nicht mehr benutzen möchte, Get-Childitem aber zu lang findet, kann man auch den Alias gci nehmen, oder auch ls, wenn man mehr an Shells wie die Bash gewöhnt ist. Eine Einschränkung hat die Geschichte mit den Aliasen allerdings: Parameter zu den Befehlen kann man nicht so einfach in die PowerShell übertragen. Der Befehl dir /s klappt nicht, stattdessen muss man den Parameter -recurse nehmen, wenn man Unterordner in die Auflistung mit einbeziehen will.

PS C:\Users\Christian> dir -recurse
 
    Verzeichnis: C:\Users\Christian
 
Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d-r--        28.05.2011     15:56            Contacts
d-r--        27.06.2011     11:37            Desktop
d-r--        05.06.2011     22:49            Documents
d-r--        12.07.2011     21:20            Downloads
[...]

Wie sucht man nun in der PowerShell? Der Befehl Get-ChildItem C:\ listet einfach alle Dateien im Wurzelverzeichnis C: auf. Wie schon oben erwähnt, kann man mit dem Parameter -recurse auch Dateien in Unterordnern auflisten.
Dabei kann man auch nach bestimmten Dateien suchen. Der folgende Befehl listet zum Beispiel alle Dateien auf, auch die in den Unterordnern, die ein „python“ im Namen tragen:

gci -recurse | Where-Object {$_.Name -like "*python*"}

Man kann aber nicht nur nach Namen sortieren, sondern auch nach bestimmten Endungen:

gci  $env:windir -recurse | Where-Object {$_.Extension -eq ".dll"}

Leitet man die Ausgabe der Suche nach Format-Table oder Select-Object um, dann bekommt man sie schön vorsortiert:

PS C:\Users\Christian> gci  $env:windir -recurse `
>> | Where {$_.Extension -eq ".dll"} `
>> | Format-Table FullName, CreationTime, Length
>>
 
FullName                               CreationTime                 Length                                         
--------                               ------------                 ------                                                 
C:\Windows\RtlExUpd.dll                 15.11.2010 07:59:44         1251944                                                
C:\Windows\twain.dll                    10.06.2009 23:41:17           94784                                              
C:\Windows\twain_32.dll                 14.07.2009 02:14:33           51200  
C:\windows\assembly\GAC\Microsoft.In... 14.07.2009 06:42:34          356352
C:\windows\assembly\GAC\Microsoft.In... 14.07.2009 06:42:34          516096
C:\windows\assembly\GAC\Microsoft.In... 05.06.2011 22:27:52            6656
C:\windows\assembly\GAC\Microsoft.St... 05.06.2011 22:29:56           22552                                           
[...]

Hier ist allerdings ein kleiner Exkurs zur formatierten Ausgabe von Format-Table nötig: Wenn der Inhalt der Spalte breiter bzw. länger ist, als die Spalte selbst, dann wird der Inhalt abgeschnitten und der Rest durch drei Punkte dargestellt. Man kann also nicht wirklich erkennen, wie es in der Spalte weiter geht. Übergibt man die Ausgabe in eine Textdatei, in der die Spalten ja breiter sein könnten, da sie nicht durch die Konsole begrenzt wird, bekommt man dasselbe Ergebnis. Der AutoSize-Parameter von Format-Table hilft leider auch nicht weiter, weil er gerade mal nur so viele Spalten darstellt, wie sie in die Konsole passen. Alles was darüber hinaus geht, lässt er weg. Auch diese Spalten landen nicht in der Ausgabedatei. Die Lösung hier heißt Strings, da Strings nicht den selben Beschränkungen wie andere Objekte der PowerShell unterliegen:

PS C:\Users\Christian> gci $env:USERPROFILE -r `
>> | Where {($_.lastwritetime -gt "2011-06-20")} `
>> | Format-Table -Property * -AutoSize `
>> | Out-String -Width 4096 `
>> | Out-File $env:USERPROFILE\log.txt
>>

Lässt man das Cmdlet Out-File am Ende weg, um die Ausgabe direkt in das Terminal umzuleiten, sieht das Ergebnis auf Grund der Beschränkungen durch die Konsole sehr wüst aus. Die Text-Datei kann sich aber sehr gut sehen lassen.

Doch zurück zum eigentlichen Thema: Nun sieht Where-Object doch recht kompliziert aus. Anstatt die Ausgabe des Get-ChildItem-Befehls an ein Where-Objekt weiterzuleiten, stellt der Befehl selbst Parameter zur Sortierung bereit. Eine vereinfachte Schreibweise, in der statt -recurse einfach nur -r geschrieben wird, ist dann zum Beispiel:

Get-ChildItem -r -filter *.dll -path $env:windir

Ersetzt man -filter durch -exclude schließt man Dateien von der Suche aus. Davon abgesehen kann man auch die Parameter-Bezeichnungen wie -filter oder -path ganz weglassen:

gci -recurse $env:USERPROFILE *python*

Wann nützt aber eine Umleitung in ein Where-Objekt? Zum Beispiel dann, wenn man nur Dateien auflisten will, die eine bestimmte Größe überschreiten,

Get-ChildItem | Where-Object {$_.length -gt 4000}

oder Dateien, die nach einem bestimmten Datum erstellt worden sind, in diesem Fall nach dem 20.06.2011:

Get-ChildItem | Where-Object {($_.lastwritetime -gt "2011-06-20")}

Das sind beides wichtige Befehle, wenn man sich mal wieder fragt, warum der Fileserver innerhalb kürzester Zeit voll gelaufen ist, bzw. welche großen Dateien auf dem Server schon seit ewigen Zeiten nicht mehr angefasst worden sind. In diesem Zusammenhang habe ich im Blog von Christian Jäckle einen Befehl gefunden, der alle Dateien in einem Verzeichnis anzeigt, die in den letzten 15 Minuten bearbeitet wurden:

gci -r C:\ | Where-Object {$_.LastWriteTime -gt (Get-Date).AddMinutes(-15)} | export-csv $env:userprofile\Log.csv -notype

Mit dem Cmdlet Get-Date wird die aktuelle Zeit ermittelt, von der 15 Minuten abgezogen werden. Das Ergebnis wird in eine CSV-Datei im User-Verzeichnis geschrieben. Den Pfad zum User-Verzeichnis liefert die Umgebungsvariable userprofile, die der Ausdruck $env:userprofile liefert.
Welche Umgebungsvariablen es überhaupt gibt, liefert übrigens auch der Befehl Get-ChildItem:

Get-Childitem env:
 
Name                           Value
----                           -----
ALLUSERSPROFILE                C:\ProgramData
APPDATA                        C:\Users\Christian\AppData\Roaming
CommonProgramFiles             C:\Program Files\Common Files
COMPUTERNAME                   KDBSH4-PC
ComSpec                        C:\windows\system32\cmd.exe
[...]

Aber das ist noch nicht alles, was Get-ChildItem auflisten kann. Man kann auch Registry-Einträge anzeigen lassen, um sie anschließend eventuell zu bearbeiten. Der folgende Befehl listet die installierte Software des Rechners auf, zumindest diejenige, die auch in die Registry eingetragen wurde:

Get-ChildItem hklm:\software

Geschrieben in Powershell, Windows