Mit powershell eine Textdatei nach präfix aufteilen, ist das möglich?
Moin!
Ich fange gerade erst an, powershell an zu wenden. Die ersten Schritte sind ja meistens die schwersten.
Wenn ich eine Textdatei habe, die zb so aufgebaut ist:
[0102030405][0707080910][1112]
( von solchen Eintragungen habe ich mehrere 1000 Werte, in verschiedenen Kombinationen, aber pro zeile, immer zb 3 Klammern)
Gibt es irgendwie die Möglichkeit, diese Textdatei in viele kleine auf zu teilen?
Am liebsten soll eine zeile( also eine Eintragung die zb immer 3 Klammern gross ist) in eine neue Datei geschrieben werden.
Die neue Textdatei hat den Namen der ersten zahl aus zb der ersten 2 Klammern. Also in dem Fall 0107( eine Zahl ist immer 2stellig.
In die Datei 0107 komm alle Zeilen aus der Textdatei, welche 01 in der ersten Klammer und 07 in der zweiten Klammer haben.
So wird das mit allem zeilen gemacht.
Am besten wäre natürlich eine Erklärung, dann lerne ich es gleich und am besten einmal mit 2 Klammern als präfix und einmal mit 3 Klammern.
Ich hoffe das ist verständlich genug.
Wenn das funktioniert wäre das echt genial!
Lg und danke!!
3 Antworten
Ich finde es immer wieder Amüsant, welch Verrenkungen gestandene Programmierer aus anderen Sprach veranstalten, wenn es um Powershell geht😅.
...dabei ist die Sache ganz einfach wieder einmal arbeiten wir mit Select-String und RegEx.
Um jeweils die ersten beiden Ziffern nach einer "[" zu extrahieren basteln wir einen einen RegexPattern:
- wir wollen zwei Ziffer (digits), Das Pattern dafür '\d{2}'
- spezielle Bedingung: schau zurück (look behind) ob vor den Ziffern eine öffnende Eckige Klammer steht : '(?<=\[)' ... ein Lookbehind captured nicht (erscheint nicht im Ergebnis)
RegEx-Glossar:
Das bauen wir Mal in eine TestZeile ein, welche die Matches anzeigt:
('[0102030405][0707080910][1112]'|Select-String '(?<=\[)\d{2}' -a).matches
ein Array von einigermaßen aussagekräftigen Objekten. Wir benötigen nur die Values von Arrayelement 0 und 1 ,welche wir einfach plump mit -join '' zu einen String vereinen.
(('[0102030405][0707080910][1112]'|Select-String '(?<=\[)\d{2}' -a).matches.value[0,1]) -join ''
Was für eine Zeile Klappt können wir auch für jeder Zeile, welche mit Get-Content gelesen wurde, veranstelten:
test.txt
[0102030405][0707080910][1112]
[0101][0707060][183848]
[01111111][03061][4712]
[02222][15894][258914]
Auf gehts:
gc 'test.txt'|%{(($_|Select-String '(?<=\[)\d{2}' -a).matches.value[0,1]) -join ''}
...soviel erstmal zur "(Gedanken)Entwicklung" in Quick&Dirty.
Jetzt mal die "hübsche" Variante:
Get-Content 'test.txt'|
ForEach-Object{
$MeineFunde = $_|
Select-String -Pattern '(?<=\[)\d{2}' -AllMatches # finde alle Teile der Zeile, welche folgendermaßen aussehen: "[2Ziffern", ohne die Klammer"["" zurückzuliefern
$FirstTwoValues = $MeineFunde.Matches.Value|
Select-Object -First 2 #Dienst nach Vorschrift :) ... die ersten 2 Values auswählen (ist das Gleiche wie: .Matches.Value[0,1])
$FileName = 'Meine Zeilen {0}.txt' -f $($FirstTwoValues -join '') #Bastle mit dem Formatoperator einen hübschen Dateinamen
#Jetzt hängen wir die aktuelle Zeile (in $_) nur noch an die für die Zeile Zuständige Datei
$_ >> $FileName
}
...und noch mal der "Klammersack" kurz und schmerzfrei 🤮:
gc 'test.txt'|%{ $_ >> $('Meine Zeilen {0}.txt'-f $((($_|Select-String '(?<=\[)\d{2}' -a).matches.value[0,1]) -join ''))}
Glossar
- https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-5.1#powershell-redirection-operators
- https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/select-object?view=powershell-5.1#-first
- https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-arrays?view=powershell-5.1#special-index-tricks
- https://learn.microsoft.com/de-de/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-5.1#format-operator--f
- https://learn.microsoft.com/de-de/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-5.1#subexpression-operator--
- https://learn.microsoft.com/de-de/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-5.1#_
...das übrige hatten wir ja schon: https://www.gutefrage.net/frage/textdatei-nach-klammer-groesse-sortieren#answer-488562528
>> 'Dateiname" ist eine ganz einfache Ausgabeumleitung https://ss64.com/ps/syntax-redirection.html
'Ein Text schreiben' > 'DemoDatei.txt'
'Ein Text anhaengen' >> 'DemoDatei.txt'
Mein Script betreffend:
Die Datei Test.txt und das Powershellscript (Demo.ps1 ...oder wie auch immer Du es nennst) müssen im gleichen Ordner sein. die Zieldateien werden im gleichen Ordner erzeugt.
Ich habe mal die Erzeugung des ZielDateinamen so geändert, das die Dateien auf den Desktop geschrieben werden und etwas Rückmeldung hinzugefügt.
Write-Host Bin im Ordner: $(Get-Location) #Mal Anzeigen wo wir sind
Get-Content 'test.txt'|
ForEach-Object{
$MeineFunde = $_|
Select-String -Pattern '(?<=\[)\d{2}' -AllMatches
$FirstTwoValues = $MeineFunde.Matches.Value|
Select-Object -First 2
$FileName = '{0}\Meine Zeilen {1}.txt' -f [Environment]::GetFolderPath("Desktop"),$($FirstTwoValues -join '') #mal ZielDateien auf den Desktop...
Write-Host schreibe: $_ in die Datei: $FileName -ForeGround green #mal anzeigen was wohin geschrieben wird
$_ >> $FileName
}
Boah voll geil. Es funktioniert. Ich bin begeistert. 😁
Ich wusste nicht das .ps1 Datein, so wie eine exe funktionieren.
Ich dachte man muss alles aus dem Editor machen.
Richtig richtig geil!
Fetten Dank! Hat mir viel Arbeit erspart!
Wunderschöne Woche dir!!!😊
Am liebsten soll eine zeile( also eine Eintragung die zb immer 3 Klammern gross ist) in eine neue Datei geschrieben werden.
Du liest die komplette Datei mit Get-Content ein:
$Zeilen = Get-Content -Path deineDatei.txt
Danach machst du eine foreach-Schleife.
foreach ($Zeile in $Zeilen)
{
}
Innerhalb dieser Schleife musst du dir jetzt deinen Dateinamen "zusammenschustern".
Du willst also zuerst Einmal das 2. und 3. Zeichen haben:
$Anfang = $Zeile.Substring(1, 2)
Substring ist eine Funktion mit der du Teile eines Strings (= Textkette) ausliest, hier sagen wir bei Zeichen 1 (man beginnt bei 0 zu zählen, also ist 1 das zweite Zeichen) beginnen und dann zwei Zeichen herauslesen.
Damit haben wir also die "01" aus deinem Beispiel.
Den Anfang von der zweiten Zahl ist schwieriger zu bekommen.
Wir können aber eine ähnliche Funktion verwenden:
$Ende = $Zeile.Substring($Zeile.IndexOf("]")+2, 2)
Mit dem $Zeile.IndexOf("]")+2 haben wir gesagt, dass wir beim Substring zwei Zeichen nachder der ersten geschlossenen Klammer anfangen möchten. Danach gehen wir wieder zwei Zeichen weit.
Jetzt haben wir fast alles, was wir brauchen. Wir müssen die beiden Sachen nur noch zusammenfügen, eine Dateiendung vergeben und alles in diesen Dateien speichern:
$Dateiname = $Anfang + $Ende + ".txt"
New-Item -Path .\ -ItemType File -Name $Dateiname -Value $Zeile
Wenn man alles zusammenfasst, dann wäre der Code also:
$Zeilen = Get-Content -Path deineDatei.txt
foreach ($Zeile in $Zeilen)
{
$Anfang = $Zeile.Substring(1, 2)
$Ende = $Zeile.Substring($Zeile.IndexOf("]")+2, 2)
$Dateiname = $Anfang + $Ende + ".txt"
New-Item -Path .\ -ItemType File -Name $Dateiname -Value $Zeile
}
Geht mit Regex und Fileredirection viel geschmeidiger:
gc 'test.txt'|%{ $_ >> $('Meine Zeilen {0}.txt'-f $((($_|Select-String '(?<=\[)\d{2}' -a).matches.value[0,1]) -join ''))}
Ist natürlich der viel schönere Weg aber ob das für AnfängerInnen verständlich ist? Ich selbst muss jedes Mal bei Regex nachschauen, aus dem Kopf bekomme ich das bei weitem nicht zusammen
Dafür habe ich einen Generator: https://img1.dreamies.de/img/620/b/twt866ko99c.jpg 😅
Also das hat mir auf jedenfall sehr geholfen, vielen Dank schon mal dafür!
Aber ich mache wohl noch irgendwas falsch...
Wie soll der path aussehen für " new item"?
Ich habe schon ein paar Sachen ausprobiert, aber keiner klappt.
LG und vielen dank, ist ne mega geile Erklärung!
Mit Path gibst du ja nur an, wo du speichern willst.
".\" sagt im aktuellen Ordner. Wenn das nicht klappt, dann hast du dort möglicherweise keine Schreibrechte. Dann solltest du wechseln. Alternativ kannst du auch ganz normal einen Pfad angeben, z.B. "C:\Meine Dokumente".
Wichtig ist, dass du einen Ordner angibst.
Ah ok super, jetzt klappt es.
Das mit dem aktuellen Ordner ist gut zu wissen!
Das Problem, er nimmt immer nur eine Zeile, und erstellt dafür eine Textdatei. Wenn eine andere Zeile mit den selben 2 zahlen anfängt, dann schreibt er sie nicht zusätzlich in die Datei mit den 2 präfixen, sondern ein Fehler, mit " ist bereits vorhanden"
Lg
Dass das nicht klappt ist klar, haben wir im Programm ja nicht definiert.
Wir müssen also prüfen ob die Datei vorhanden ist. Wenn ja, ergänzen wir, wenn nein, dann erstellen wir die Datei mit Inhalt:
$Dateipfad = "./" + $Dateiname
IF (Test-Path $Dateipfad)
{
Add-Content -Path $Dateipfad -Value $Zeile
}
Else
{
New-Item -Path .\ -ItemType File -Name $Dateiname -Value $Zeile
}
klar, das geht. mit powershell ist es möglich eine textdatei nach präfix aufzuteilen, ich schreibe dir mal ein beispiel-script, dann kannst du anschauen ob es so geht wie du es willst:
$InputFile = "C:\input.txt"
$OutputPath = "C:\output\"
Get-Content $InputFile | ForEach-Object {
$line = $_
$prefix = $line.Substring(1,4)
$filename = $prefix + ".txt"
$filepath = Join-Path $OutputPath $filename
Add-Content $filepath $line
}
Moin, also ich habe es getestet und weiss jetzt nicht ob ich was falsch mache, aber ich glaube er sucht vorhandene Datein in die er das speichert oder?
Er sollte aber von vorn herein diese Textdatei erst erstellen.
Also theoretisch beginnt der erste Wert jeweils eine neue Textdatei und der 2te Werte wird dann jeweils hinzugefügt. Oder mache ich einen Fehler.
Es kommt der Fehler" add-content : im angegbenen pfad .... ist kein Objekt vorhanden...
Aber vielen dank schon mal!!!
versuche es mal so:
$InputFile = "C:\input.txt"
$OutputPath = "C:\output\"
Get-Content $InputFile | ForEach-Object {
$line = $_
$prefix = $line.Substring(1,4)
$filename = $prefix + ".txt"
$filepath = Join-Path $OutputPath $filename
If (!(Test-Path $filepath)) {
New-Item -Path $filepath -ItemType File
}
Add-Content $filepath $line
}
Moin! Also er erstellt eine Datei mit den ersten 4 Zeichen, also [13][664488...
Ist der erste Wert in der Textdatei und die Datei die erstellt wird heisst "13]["
Aber es wird nichts eingetragen, also sie ist 0kb gross und es kommt der Fehler:
AddContent: die dynamischen Parameter für das vieler können nicht abgerufen werden. ....platzhalterzeichenmuster ungültig...
oh Gott:
schon mal was von Redirection >> gehört? 😄
Der Output enthält keine Unicode Zeichen oder Ähnliches, ergo brucht man auch nicht mit Append-Content herumrödeln. Der testet bei jedem Durchlauf ob die Datei existiert, bevor er zugreift. Overkill am Dateisystem. Blos gut das wir keine Floppys mehr haben... säg, säg
Steht "$_ >> $FileName" für den output? Ich verstehe noch nicht so ganz wie die Datein erstellt werde.
Es werden nämlich irgendwie keine output Datein erstellt.
Auch nicht in dem powershell Ordner und auch nicht wenn ich es als Admin ausführe.
Lg