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
[...] |
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*"} |
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"} |
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
[...] |
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
>> |
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 |
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* |
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} |
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")} |
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 |
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
[...] |
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 |
Get-ChildItem hklm:\software