Benutzer mit den meisten Antworten
Alle Files auf dem Server durchgehen

Frage
-
Hallo zusammen,
ich darf ( oder muss :-) )alle Files auf unserem FileServer durchgehen und dann den Fullname in ein Log-File schreiben wenn das File einem bestimmten Muster entspricht.
Mittlerweile konnte ich über .matches den Filename entsprechend dem erforderlichen Muster festmachen, das klappt auch ganz gut, nur jetzt habe ich ein paar Fragen :-)
Ich suche wie folgt:
#Suche durchführen foreach ($file in Get-ChildItem -Path $directory -Recurse -Force -ErrorAction "Continue") { #ist ein Fehler aufgetreten? if (!($?)) { "Fehler: $($error[0])" } #wenn Extension stimmt ... if ($file.Extension -match $extension) { #prüfen ob Filename zum Muster passt if ($file.FullName -match $filter) { "{0};{1};{2}" -f $file.name,$file.Directory,$file.Length } } }
Jetzt habe ich das Problem das ich erst mal eine Menge Fehlermeldungen bekomme (Kurzform):
PermissionDenied: (C:\Windows\Syst...es\WMI\RtBackup:String) [Get-ChildItem], UnauthorizedAccessException
Diese Meldung kommt nicht von meiner Abfrage ob ein Fehler aufgetreten ist, diese kommt scheinbar von "Get-ChildItem" da erst später meine Abfrage auf den Fehler stattfindet.
Wie es aussieht, liest "Get-ChildItem" erst mal alle EInträge aus dem FileSystem aus und geht dann über foreach durch das Ergebnis.
Folgende Fragen fallen mir jetzt mal ein:
-wie kann ich den Fehler in "Get-ChildItem" abfangen
-ist diese Abfrage bei einer Anzahl von über 10 Millionen Files nicht gefährlich wegen Datenhaltung aus "Get-ChildItem"
-wie könnte ich alle Fehler abfangen um diese in einem File zu schreibenIch schreibe mir das Script gerade auf einem normalen Laptop (noch nicht auf dem Server) und bin in Sachen PowerShell noch Anfänger, daher wollte ich nicht gleich am echten System testen.
Wie könnte man das Vorhaben alle Files die einem Muster zu erkennen, den Fullname in ein LogFile zu schreiben dabei aber auch alle anderen Fehler (Access Denied etc) in einem Fehler-LogFile sichern ohne den Server "in den Wahnsinn" zu treiben?
Ich finde PowerShell für's erste extrem stark, man kann vieles machen, muss nur langsam in die Matherie rein kommen und wäre Euch über ein paar Infos und Tipps zu meinem ersten Einsatz von PS sehr dankbar.
Viele Grüsse,
Maximilian
Antworten
-
> ist diese Abfrage bei einer Anzahl von über 10 Millionen Files nicht gefährlich wegen Datenhaltung aus "Get-ChildItem"
Mir ist nicht ganz klar was du mit "gefährlich" meinst. Es ist auf jeden Fall so, das du mit der "Foreach ( x in gc y)" -Variante jede Menge Speicher benötigst, da zuerst alle Dateien eingelesen werden und dann erst gefiltert werden.
Speicherschonend und auch schneller ist es in diesem Fall die Pipeline zu benutzen. Hier werden die Filesystemobjekte nacheinander angefasst und gleich "verarbeitet".
>wie kann ich den Fehler in "Get-ChildItem" abfangen
>wie könnte ich alle Fehler abfangen um diese in einem File zu schreibenUm den Fehler abzufangen und in ein Log zu schreiben, unterdrückst du zuerst die direkte Ausgabe derselben mit -ErrorAction "SilentlyContinue" und fragst daraufhin die $error - Variable ab und speicherst den Inhalt Zeile für Zeile in eine Datei mit "add-content". (Grundsätzlich gibt es übrigens noch andere Methoden der Fehlerbehandlung (siehe hier), mit denen man mal ein bischen rumspielen sollte, da nicht jede Variante für jeden Fall geeignet ist.)
$errorlog = "d:\errorlog.txt" $directory = "c:\" $extension = "txt" $filter = "do" Get-ChildItem -Path $directory -Recurse -Force -ErrorAction "SilentlyContinue" | ForEach-Object { if ($error) { foreach ($message in $error) { write-host "Fehler: $message" # Zeile löschen um Fehler ausschließlich ins Log zu schreiben add-content $errorlog $message } $error.Clear() } #wenn Extension und Filter übereinstimmen ... if (($_.Extension -match $extension)-and($_.FullName -match $filter)) { "{0};{1};{2}" -f $_.name,$_.Directory,$_.Length } }
Noch eine Anmerkung zu der Filterung:($_.FullName -match $filter)
".FULLname" enthält den kompletten Pfad der Datei, d.h. sobald dein String aus $Filter irgendwo im Pfad vorkommt wird "Wahr" zurückgemeldet. Falls du (was ich annehme) nur die Dateinamen überprüfen willst, solltest du "$_.name" verwenden.
Grüße, Denniver
http://bytecookie.wordpress.com/
- Bearbeitet Denniver ReiningMVP, Moderator Donnerstag, 7. Juli 2011 15:45 Ergänzungen
- Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:30
-
Um export-csv zu benutzen müsste man die Pipeline umbauen, einfacher gehts so:
if (($_.Extension -match $extension)-and($_.Name -match $filter)) { add-content "c:\temp\result.csv" ($_.name+";"+$_.Directory+";"+$_.Length) }
Grüße, Denniver
http://bytecookie.wordpress.com/- Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:30
-
Du musst das Objekt in einen String umwandeln damit die Addition klappt:
$temp += $_.name.ToString() + ";"
Grüße, Denniver
http://bytecookie.wordpress.com/- Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:30
-
>Du sagst das man die Pipeline umbauen muss, nur wie kann ich beim Get-Childitem (hier bekomme ich ja die Objekte)
> dann so prüfen das die Extension als auch das Muster passt?Das sähe dann so aus:
$directory = "d:\" $extension = "txt" $filter = "" Get-ChildItem -Path $directory -Recurse -Force -ErrorAction "SilentlyContinue" | Where-Object{ ($_.Extension -match $extension)-and($_.Name -match $filter)} | Select-Object Name,Directory,Length | Export-Csv -Delimiter ";" d:\test.csv
Bei diesem Aufbau ist allerdings kein "Platz" mehr für die Fehlerabfrage. Daher müssen wir dann $error am Ende des Skriptes auslesen:if ($error) { foreach ($message in $error) { write-host "Fehler: $message" add-content $errorlog $message } }
Grüße, Denniver
http://bytecookie.wordpress.com/- Bearbeitet Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:31
- Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:31
Alle Antworten
-
> ist diese Abfrage bei einer Anzahl von über 10 Millionen Files nicht gefährlich wegen Datenhaltung aus "Get-ChildItem"
Mir ist nicht ganz klar was du mit "gefährlich" meinst. Es ist auf jeden Fall so, das du mit der "Foreach ( x in gc y)" -Variante jede Menge Speicher benötigst, da zuerst alle Dateien eingelesen werden und dann erst gefiltert werden.
Speicherschonend und auch schneller ist es in diesem Fall die Pipeline zu benutzen. Hier werden die Filesystemobjekte nacheinander angefasst und gleich "verarbeitet".
>wie kann ich den Fehler in "Get-ChildItem" abfangen
>wie könnte ich alle Fehler abfangen um diese in einem File zu schreibenUm den Fehler abzufangen und in ein Log zu schreiben, unterdrückst du zuerst die direkte Ausgabe derselben mit -ErrorAction "SilentlyContinue" und fragst daraufhin die $error - Variable ab und speicherst den Inhalt Zeile für Zeile in eine Datei mit "add-content". (Grundsätzlich gibt es übrigens noch andere Methoden der Fehlerbehandlung (siehe hier), mit denen man mal ein bischen rumspielen sollte, da nicht jede Variante für jeden Fall geeignet ist.)
$errorlog = "d:\errorlog.txt" $directory = "c:\" $extension = "txt" $filter = "do" Get-ChildItem -Path $directory -Recurse -Force -ErrorAction "SilentlyContinue" | ForEach-Object { if ($error) { foreach ($message in $error) { write-host "Fehler: $message" # Zeile löschen um Fehler ausschließlich ins Log zu schreiben add-content $errorlog $message } $error.Clear() } #wenn Extension und Filter übereinstimmen ... if (($_.Extension -match $extension)-and($_.FullName -match $filter)) { "{0};{1};{2}" -f $_.name,$_.Directory,$_.Length } }
Noch eine Anmerkung zu der Filterung:($_.FullName -match $filter)
".FULLname" enthält den kompletten Pfad der Datei, d.h. sobald dein String aus $Filter irgendwo im Pfad vorkommt wird "Wahr" zurückgemeldet. Falls du (was ich annehme) nur die Dateinamen überprüfen willst, solltest du "$_.name" verwenden.
Grüße, Denniver
http://bytecookie.wordpress.com/
- Bearbeitet Denniver ReiningMVP, Moderator Donnerstag, 7. Juli 2011 15:45 Ergänzungen
- Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:30
-
Hallo Denniver,
erst mal vielen Dank für die Info.
Ich werde mein Script entsprechend abändern, mit Deinem Tipp wird in diesem Fall nicht erst alle Einträge für die Files geladen und dann durchgegangen sondern sofort verarbeitet. Das ist auch das was ich sehr viel besser finde, mein Vorgehen hätte sicher den Speicher stark belastet wenn erst mal 10Mio Zeilen gelesen werden :-)
Werde das gleich mal testen.
Viele Grüsse,
Maximilian -
Hallo Denniver,
so, das ganze funktioniert bestens, habe auch weitere und benötigte Funktionen soweit schreiben können.
Eines aber schaffe ich gerade nicht, ich will jetzt das Ergebnis (wenn also Extension und Filter passt) das ganze in ein CSV-File ausgeben. Dazu habe ich
| Export-Csv -Delimiter ";" c:\temp\result.csv
verwendet.
Aber leider klappt es nicht, wäre über ein Tipp dazu noch sehr sehr dankbar da ich mittlerweile auf der Zielgeraden bin.
Viele Grüsse,
Maximilian -
Um export-csv zu benutzen müsste man die Pipeline umbauen, einfacher gehts so:
if (($_.Extension -match $extension)-and($_.Name -match $filter)) { add-content "c:\temp\result.csv" ($_.name+";"+$_.Directory+";"+$_.Length) }
Grüße, Denniver
http://bytecookie.wordpress.com/- Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:30
-
Hallo Denniver,
da ich mittlerweile mehr als nur die im Beitrag genannten Variablen speichern will, setze ich erst eine Variable $temp, danach weise ich dem String dann die Werte in den verschiedenen Zeilen zu:
$temp = ""
...
$temp += $_.Name + ";"
...
$temp += $_.Directory + ";"
uswAber nun bekomme ich den Fehler
Method invocation failed because [System.IO.DirectoryInfo] doesn't contain a method named 'op_Addition'.Verwende ich nur ein ";" geht es.
Bin jetzt etwas verwirrt :-)
Viele Grüsse,
Maximilian
-
Du musst das Objekt in einen String umwandeln damit die Addition klappt:
$temp += $_.name.ToString() + ";"
Grüße, Denniver
http://bytecookie.wordpress.com/- Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:30
-
Hallo Denniver,
ist jetzt nicht für mein Script wichtig, aber aus Interesse habe ich versucht das ganze etwas umzubauen damit Export-Csv verwendet werden kann.
Leider blicke ich da nicht so durch, denn wenn ich es so umgebaut habe das Export-Csv verwendet werden kann, klappt es mit der Abfrage nicht mehr ob der Dateiname meinem Muster entspricht, lediglich auf die Extension konnte ich einschränken.
Du sagst das man die Pipeline umbauen muss, nur wie kann ich beim Get-Childitem (hier bekomme ich ja die Objekte) dann so prüfen das die Extension als auch das Muster passt?
Gruss,
Maximilian -
>Du sagst das man die Pipeline umbauen muss, nur wie kann ich beim Get-Childitem (hier bekomme ich ja die Objekte)
> dann so prüfen das die Extension als auch das Muster passt?Das sähe dann so aus:
$directory = "d:\" $extension = "txt" $filter = "" Get-ChildItem -Path $directory -Recurse -Force -ErrorAction "SilentlyContinue" | Where-Object{ ($_.Extension -match $extension)-and($_.Name -match $filter)} | Select-Object Name,Directory,Length | Export-Csv -Delimiter ";" d:\test.csv
Bei diesem Aufbau ist allerdings kein "Platz" mehr für die Fehlerabfrage. Daher müssen wir dann $error am Ende des Skriptes auslesen:if ($error) { foreach ($message in $error) { write-host "Fehler: $message" add-content $errorlog $message } }
Grüße, Denniver
http://bytecookie.wordpress.com/- Bearbeitet Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:31
- Als Antwort markiert Denniver ReiningMVP, Moderator Samstag, 9. Juli 2011 13:31
-
Hi Denniver,
vielen Dank, ich sehe aber das ich das ganze (wie die Pipeline funktioniert) noch nicht ganz verstanden habe :-)
Nun ja, hoffe das wird bald besser...
Derzeit bin ich gerade am schauen wie ich ein eigenes Objekt erstellen kann in dem ich z.Bsp. die Eigenschaften selber verwalten kann. Soll heissen, ich stelle mir vor ein eigenes Objekt zu erstellen, die einzelnen Properties wie FullName, Name, Directory, Lenght (und was sonst alles benötigt wird) zu erstellen, wenn möglich zu definieren welche Eigenschaft ein Property hat (also Integer, String, Boolean etc) um dann Werte aus Verschiedenen Abfragen (hier in erster Linie Get-ChildItem) benötigte Werte dem eigenen Objekt zuzuweisen.
Wenn dann alle Daten zusammen sind, kann ich ja das Objekt komplett weiter verarbeiten, z.Bsp. dann auch an Export-Csv übergeben.
Im Moment bin ich wieder von Export-Csv abgegangen da auch Zahlen mit Hochkomma in der Zieldatei maskiert. Mache ich das selber, kann ich beim String die Hochkommas verwenden und bei Integer dann weglassen.
Ist nur eine Schönheitsgeschichte, aber macht das lesen des Csv-File einfacher.
Das eigentliche Script ist nun am laufen, jetzt geht es an die Kür, oder besser gesagt, jetzt bin ich mit PowerShell angefressen und versuche mehr darüber zu lernen, denn das Teil ist genial um auf den Servern gewisse Dinge machen zu können. Die Scripte sind zudem auch von anderen Kollegen anpassbar, was bei einem kompilierten Exe eben nicht mehr geht. Dass macht es interessant :-)
Mal schauen ob das mit den Objekten so klappt wie ich es mir vorstelle.
Viele Grüsse,
Maximilian