Mit powershell eine Textdatei nach präfix aufteilen, ist das möglich?

3 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

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

...das übrige hatten wir ja schon: https://www.gutefrage.net/frage/textdatei-nach-klammer-groesse-sortieren#answer-488562528

Woher ich das weiß:eigene Erfahrung – Ich mach das seit 30 Jahren

Ranjahh 
Fragesteller
 09.02.2023, 19:30

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

0
Erzesel  09.02.2023, 20:40
@Ranjahh

>> '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
 }
0
Ranjahh 
Fragesteller
 13.02.2023, 21:37
@Erzesel

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!!!😊

0
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

}

Ranjahh 
Fragesteller
 08.02.2023, 20:19

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!


0
TheFamousSpy  08.02.2023, 20:21
@Ranjahh

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.

0
Ranjahh 
Fragesteller
 08.02.2023, 20:53
@TheFamousSpy

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

0
TheFamousSpy  09.02.2023, 08:40
@Ranjahh

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
}

0
Erzesel  08.02.2023, 21:01

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 ''))}
0
TheFamousSpy  09.02.2023, 08:41
@Erzesel

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

0

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
}

Ranjahh 
Fragesteller
 08.02.2023, 18:53

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!!!

0
karotte1386824  08.02.2023, 20:30
@Ranjahh

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

}

0
Ranjahh 
Fragesteller
 08.02.2023, 21:14
@karotte1386824

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...

0
Erzesel  08.02.2023, 21:19
@karotte1386824

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

0