Benutzer mit den meisten Antworten
txt Dateien nach Teil des Dateinamens filtern

Frage
-
Hallo,
ich habe einen Ordner mit sehr vielen txt Dateien. Format: LIST_Zeitstempel_ID also zum Beispiel: LIST_191108_A0234. Zeitstempel beruht auf dem CreationTime
Von jeder ID kann es mehrere Listen geben, die alle ein unterschiedlichen Zeitstempel haben. Ich möchte nun gerne für jede ID die aktuellste (nach Zeitstempel bzw Creationtime) Datei in eine neue Datei (LIST_ALL.txt) schreiben.
Umsetzungsidee wäre folgende:
Mit Get-ChildItem alle .txt Files holen und formatieren(1), so dass ich im nächsten Schritt mit Replace()(2), die ID ermitteln kann. Dann mit Get-Unique alle doppelten löschen(3). Dann wieder Get-ChildItem(4*verschiedene ID-Gruppen) mit Filter ID und diese nach Creationtime sortieren(5) und in die neue Liste schreiben.
Meine Frage wäre: Wie kann man das effizienter machen?
Momentan würde ich mit dieser Strategie ca. 5*verschiedene ID-Gruppen alle .txt Files durchlaufen. Kann man nicht gewisse Schritte kombinieren z.B. das erste Get-Childitem und Repalce in einem Schleifendurchlauf?
Wäre super wenn jemand Verbesserungsvorschläge hat.
mfg werdas34
Antworten
-
Mit Get-ChildItem alle .txt Files holen und formatieren(1), so dass ich im nächsten Schritt mit Replace()(2), die ID ermitteln kann. Dann mit Get-Unique alle doppelten löschen(3). Dann wieder Get-ChildItem(4*verschiedene ID-Gruppen) mit Filter ID und diese nach Creationtime sortieren(5) und in die neue Liste schreiben.
'LIST_191108_A0234'.Substring(5,6)
Live long and prosper!
(79,108,97,102|%{[char]$_})-join''
- Als Antwort markiert Denniver ReiningMVP, Moderator Sonntag, 17. November 2019 15:35
-
für ein Datumsformat musst du mal hier sehen:
https://stackoverflow.com/questions/51224/regular-expression-to-match-valid-dates/8768241#8768241Den Ausdruck must du nur umformatieren;-).
Ansonsten dann eben "LIST_<DatumAusdruck>_TaktInfo_.{5}\.txt"
Ohne Datumsprüfung währe der Ausdruck
"LIST_\d{6}_TaktInfo_.{5}\.txt"
- Als Antwort markiert Denniver ReiningMVP, Moderator Sonntag, 17. November 2019 15:34
-
Gibt es von Get-Date eine Methode oder Möglichkeit zu prüfen, ob die 6 Zahlen sinnvoll sind? Sowas wäre sinnfrei "191312", da es keinen 13. Monat gibt.
$FileDate = <...> # woher auch immer das Dateidatum kommt :-) [ref]$Date = Get-Date [bool]$ValidDate = [DateTime]::TryParseExact( $FileDate, 'yyMMdd', $null, 'None', $date )
Greetings/Grüße, Martin - https://mvp.microsoft.com/en-us/PublicProfile/5000017 Mal ein gutes Buch über GPOs lesen? - http://www.amazon.de/Windows-Server-2012--8-Gruppenrichtlinien/dp/3866456956 Good or bad GPOs? My blog - http://evilgpo.blogspot.com And if IT bothers me? Coke bottle design refreshment - http://sdrv.ms/14t35cq
- Als Antwort markiert Denniver ReiningMVP, Moderator Sonntag, 17. November 2019 15:34
Alle Antworten
-
Mit Get-ChildItem alle .txt Files holen und formatieren(1), so dass ich im nächsten Schritt mit Replace()(2), die ID ermitteln kann. Dann mit Get-Unique alle doppelten löschen(3). Dann wieder Get-ChildItem(4*verschiedene ID-Gruppen) mit Filter ID und diese nach Creationtime sortieren(5) und in die neue Liste schreiben.
'LIST_191108_A0234'.Substring(5,6)
Live long and prosper!
(79,108,97,102|%{[char]$_})-join''
- Als Antwort markiert Denniver ReiningMVP, Moderator Sonntag, 17. November 2019 15:35
-
Wenn die Dateinamen alle das gleiche Format haben, kannst Du die .subString() Methode benutzen. z.B.:
'LIST_191108_A0234'.Substring(5,6)
...und wenn die Längen variieren und das Trennzeichen nicht, kannst Du mit -split die einzelnen Teile bekommen:
'LIST_191108_A0234'.Split('_')
Evgenij Smirnov
-
Get-ChildItem brauchst Du nur einmal. ;-) Du könntest dann mittels calculated properties die IDs und die Zeitstempel als separate Properties erzeugen, nach denen Du dann sortieren, gruppieren und "uniquen" kannst. Wenn die Dateinamen alle das gleiche Format haben, kannst Du die .subString() Methode benutzen. z.B.:
'LIST_191108_A0234'.Substring(5,6)
Ist aber keine Effizienzsteigerung, oder? Ich mein mit Get-ChildItem bekomme ich ein Array. Ob ich nun ein weiters Array (mit Get-ChildItem) nach meinen Wünschen erstelle, oder ob ich vom alten Array mittels -match meine Sachen gruppiere. Der Aufwand ist der selbe.
Oder verstehe ich dich falsch?
-
Da Du absolut nichts von Deinem Code gezeigt hast, sondern nur eine vage Beschreibung lieferst, kann man das nicht sagen. So lange Du ein zweiter Mal ein Get-ChildItem brauchst, glaube ich schon, dass mein Ansatz effizienter wäre. Ist denn Effizienz ein Problem bei Deinem Code? Läuft Dein Code nicht schnell genug? Verbraucht Dein Code zu RAM? Wenn das alles nicht der Fall ist und Dein Code funktioniert, dann ist doch alles gut. Wenn Du neugierig bist, ob man diese Aufgabe eleganter lösen kann, als Du es getan hast, wirst Du wohl Deinen Code hier posten müssen und wir gucken mal .... ;-)
Live long and prosper!
(79,108,97,102|%{[char]$_})-join''
-
Es gibt keinen vorzeigbaren Code. Nur die Strategie. Ich würde gern jetzt schon möglichst effizient das umsetzen. Auf den Server sind zurseit ca. 1000 txt Files. Gedacht sind später mindestens 30.000 FIles. Und da macht jeder extra Durchlauf schon einen Unterschied.
Was man auch sagen muss. Das Skript wird einmal in der Nacht ausgeführt, da der Server nur zu einem Zeitpunkt neue txt Files bekommt.
Ich werde ihn Montag dann man hochladen. :D
-
OK, dann bleibt's bei dem Hinweis aus meiner ersten Antwort. Dateisystem-Operationen sind üblicherweise aufwändig und träge und wir versuchen sie auf ein Minimum zu reduzieren. Wenn Du also ein zweites Get-ChildItem brauchst, hast Du vermutlich schon 'ne Bremse eingebaut. ;-)
Wenn es um sehr viele Dateien geht, kann man sogar überlegen, robocopy für seine Zwecke einzuspannen. Das ist viel schneller als pures Powershell und man kann es z.B. auch dafür benutzen, nur die Dateinamen einzusammeln. ;-) ;-)
Live long and prosper!
(79,108,97,102|%{[char]$_})-join''
-
Jetzt verstehe ich deine Argumentation. Mir gings nur um die eigentlichen Durchläufe. Habe jetzt den Zugriff auf das Dateisystem komplett vergessen. Jetzt machts natürlich viel SInn. :D
Und mit Robocopy hast du mich neugierig gemacht. Kenne dieses Tool und arbeite grade, um für mich persönlich ein nettes Backup Tool zu schreiben. Wie bekomme ich nur die Dateinamen? Habe jetzt schon paar mal die Optionen durch und nichts dementsprechend gefunden. Und kann man dann mit Robocopy auch die Creationtime abfragen?
-
Und mit Robocopy hast du mich neugierig gemacht. Kenne dieses Tool und arbeite grade, um für mich persönlich ein nettes Backup Tool zu schreiben. Wie bekomme ich nur die Dateinamen? Habe jetzt schon paar mal die Optionen durch und nichts dementsprechend gefunden. Und kann man dann mit Robocopy auch die Creationtime abfragen?
Live long and prosper!
(79,108,97,102|%{[char]$_})-join''
- Bearbeitet BOfH-666 Samstag, 9. November 2019 16:57
- Als Antwort markiert Denniver ReiningMVP, Moderator Sonntag, 17. November 2019 15:34
- Tag als Antwort aufgehoben Denniver ReiningMVP, Moderator Sonntag, 17. November 2019 15:35
-
Vielen Dank. Habe jetzt bisschen rumgespielt. Die erste Frage ist, ob es im Endeffekt durch das parsen immer noch effizienter ist.
Hast du einen Tipp was ich als Zielpfad angeben sollte? Wenn Quelle und Ziel gleich sind funktioniert gar nichts. Und wenn ich das Ziel anders bestimme dann kommen vom Ziel noch die Dateien. Man müsste einen leeren Ordner als Ziel wählen, was auch wieder unschön ist.
Und wie splitte ich nach newline? $string.split("`n") funktioniert nicht. .split("") möchte ich nicht verwenden, da ich dann ganz viele leere Arrayeinträge habe. Unten sieht man die Ausgabe in PS.
2019/11/07 19:54:47 C:\Users\*\Desktop\Test\LIST_190911_Taktinfo_A1234.txt 2019/11/07 19:54:47 C:\Users\*\Desktop\Test\LIST_190912_Taktinfo_A1234.txt 2019/11/07 19:54:47 C:\Users\*\Desktop\Test\LIST_190912_Taktinfo_A5678.txt 2019/11/07 19:54:47 C:\Users\*\Desktop\Test\LIST_190912_Taktinfo_B1266.txt
Edit: Problem gelöst. Kein Wunder, wenn das array[0] einfach nur eine Leerzeile ausgibt. :D
- Bearbeitet werdas34 Samstag, 9. November 2019 20:29 Korrektur
-
Und wenn ich das Ziel anders bestimme dann kommen vom Ziel noch die Dateien. Man müsste einen leeren Ordner als Ziel wählen, was auch wieder unschön ist.
Ich erstelle mir üblicherweise einen neuen leeren Ordner, den ich nach getaner Arbeit wieder abräume. Aber genaugenommen muss das auch nicht sein, wenn man sich mal die Hilfe für robocopy ansieht, findet man noch die Option /XX - damit werden dann noch die eXtra Dateien ausgelassen
Und wie splitte ich nach newline?
Was und warum willst Du denn nach newline splitten?
Live long and prosper!
(79,108,97,102|%{[char]$_})-join''
-
Und wie splitte ich nach newline?
Was und warum willst Du denn nach newline splitten?
Keine Ahnung wie ich den grauen Balken oben weg bekomme, ohne das es mir die komplette Formatierung zerschießt.
Man kann ja nicht ahnen das $array[0] einfach einen leeren String zurückliefert. Dachte deshalb ich müsste ihn splitten.
Mit Robocopy bekomme ich mittels /TS den TimeStamp. Nur dieser TimeStamp bezieht sich auf das Änderungsdatum. Ist es möglich mit Robocopy das Erstelldatum abzufragen? In der Doku fand ich nichts dazu. Vielleicht kennst du einen Weg?
-
Vielleicht kennst du einen Weg?
Live long and prosper!
(79,108,97,102|%{[char]$_})-join''
-
Es gab da ein Missverständnis zwischen Auftraggeber und mir. Er hatte Testdaten zur Verfügung gestellt mit alten und neuen Dateiformaten ohne zu erwähnen, das die alten nicht mehr benötigt werden. Ich dachte deshalb ich soll die alten in die neuen Formate umwandeln. Muss ich nicht. Das Datum steht im neuem Format bereits im Filenamen drin. Muss man nur herausoperieren aus dem Namen.
Im folgenden kommt der Code wie versprochen: Kurze Erläuterung zu unteren Teil. Alle Datums die in der Zukunft liegen sollen nicht in der Liste.txt sein. Und von den aktuellen soll nur die jeweils jüngste der Id in der Liste auftauchen.
$LIST = "LIST_TaktInfo_DateiListe.txt" $FILTER_BAT = "*.bat" $FILTER_CMD = "*.cmd" $FILTER_PS1 = "*.ps1" $FILTER_LOG = "*.log" $FILTER_PPTX = "*.pptx" $FILTER_Aktuell = "*Aktuell*.txt" function Test-Filename{ param([string] $file) $splittedArr = $file.Split("_") try{ if(-Not ($splittedArr[0] -eq "LIST")){ return $false if(-Not ($splittedArr[1] -match "^\d+$")){ return $false if(-Not ($splittedArr[2] -eq "TaktInfo")){ return $false if(-Not(($splittedArr[3].Length -eq 9) -and ($splittedArr[3].EndsWith(".txt")))){ return $false } } } } }catch{ return $false } return $true } function Create-Object{ param([string] $file) $trimmed = $file.Trim() $name = $trimmed.Split("\")[-1] $id = $trimmed.Split("_")[-1] $date = $name.Split("_")[1] $obj = New-Object -TypeName PSObject $obj | Add-Member -Name "Date" -MemberType NoteProperty -Value $date $obj | Add-Member -Name "Name" -MemberType NoteProperty -Value $name $obj | Add-Member -Name "Id" -MemberType NoteProperty -Value $id return $obj } $dontSpamLog = New-Item -Path $PSSCRIPTROOT -ItemType "directory" -Name "Test" $allFiles = robocopy $PSSCRIPTROOT $("$PSSCRIPTROOT\Test") /L /NJH /NJS /NDL /NS /NC /NP /XJ /XX /XF $FILTER_BAT $FILTER_CMD $FILTER_PS1 $FILTER_LOG $FILTER_PPTX $FILTER_Aktuell $LIST Remove-Item -Path $("$PSSCRIPTROOT\Test") $allFilesInfo = @() for($i=1; $i -lt $allFiles.Length; $i++){ $name = $allFiles[$i].Split("\")[-1] if(Test-Filename -file $name){ $allFilesInfo += Create-Object -file $name } } $currentDate = Get-Date -Format "yyMMdd" $sortedDateAll = $allFilesInfo | Sort-Object -Property Date -Descending $sortedDate = $sortedDateAll | Where-Object {$_.Date -le $currentDate} $uniqueIdCollector = @() $uniqueId = @() foreach($date in $sortedDate){ if(-Not ($uniqueIdCollector.Contains($date.Id))){ $uniqueIdCollector += $date.Id $uniqueId += $date } } $uniqueId | Select-Object -ExpandProperty Name > "$($PSSCRIPTROOT)\$LIST"
Zwei Fragen:
- Kann man das Testen auf das richtige Dateiformat besser machen? Ein gültiges Format beginnt mit LIST dann Datum in 6 Zahlen, dann TaktInfo und dann eine beliebige Zeichenfolge die 5 Zeichen lang ist. Z.B.: "LIST_191107_TaktInfo_4079A.txt".
- Gibt es von Get-Date eine Methode oder Möglichkeit zu prüfen, ob die 6 Zahlen sinnvoll sind? Sowas wäre sinnfrei "191312", da es keinen 13. Monat gibt.
-
für ein Datumsformat musst du mal hier sehen:
https://stackoverflow.com/questions/51224/regular-expression-to-match-valid-dates/8768241#8768241Den Ausdruck must du nur umformatieren;-).
Ansonsten dann eben "LIST_<DatumAusdruck>_TaktInfo_.{5}\.txt"
Ohne Datumsprüfung währe der Ausdruck
"LIST_\d{6}_TaktInfo_.{5}\.txt"
- Als Antwort markiert Denniver ReiningMVP, Moderator Sonntag, 17. November 2019 15:34
-
Gibt es von Get-Date eine Methode oder Möglichkeit zu prüfen, ob die 6 Zahlen sinnvoll sind? Sowas wäre sinnfrei "191312", da es keinen 13. Monat gibt.
$FileDate = <...> # woher auch immer das Dateidatum kommt :-) [ref]$Date = Get-Date [bool]$ValidDate = [DateTime]::TryParseExact( $FileDate, 'yyMMdd', $null, 'None', $date )
Greetings/Grüße, Martin - https://mvp.microsoft.com/en-us/PublicProfile/5000017 Mal ein gutes Buch über GPOs lesen? - http://www.amazon.de/Windows-Server-2012--8-Gruppenrichtlinien/dp/3866456956 Good or bad GPOs? My blog - http://evilgpo.blogspot.com And if IT bothers me? Coke bottle design refreshment - http://sdrv.ms/14t35cq
- Als Antwort markiert Denniver ReiningMVP, Moderator Sonntag, 17. November 2019 15:34