Hinweis: Erstellung und Testung unter win10, wenn es bei win 7 Probleme gibt, bitte dem Autor mitteilen!


13. Inhalt einer Textdatei oder Logdatei nach Datumsangaben in der Datei sortieren mit Batch

Diesmal weckte eine Leseranfrage mein Interesse an einem Problem, welches nicht nur für Administratoren hilfreich sein könnte: Kann man eine Textdatei nach einem Kriterium wie Daten auf einer CSV oder einem Log nach Datum allein mit einer Batch sortieren. Der Lösungsansatz auf der Meta-Ebene ist für beliebig lange Texte ohne Doppelpunkte, die sie innerhalb von HTML,TXT,CSV oder Log-Dateien nach Datum, Uhrzeit oder alphabetisch Zeilen innerhalb eines Dokuments sortieren wollen.

Ich exerziere das exemplarisch mit den Datum in Form DD.MM.YYYY durch.


Also Mal eine Probedatei mit Inhalt, um das Problem zu verdeutlichen, die vers.txt (eben versuchstext) heißt:

25.07.1970 Manfred Thiele
21.12.2006 test
01.12.1960 gut
18.03.2012 dadum didei
07.08.1812 alter

Die soll nun Datum sortiert als Ergebnis lauten:

07.08.1812 alter
01.12.1960 gut
25.07.1970 Manfred Thiele
21.12.2006 test
18.03.2012 dadum didei

Das Gute ist erst mal, dass wir uns im Prinzip an einem festen Datum links orientieren können. Die einzelnen Buchstaben kann man also mit Variablen ansprechen. Schlechter hingegen ist es, dass die klassische For-Funktion erstmal kein Array besitzt, sondern sich Zeile für Zeile durcharbeitet.

Also muss man sich ein künstliches unsortiertes Array schaffen und dann die einzelnen Zeilen einfach wieder in der richtigen Reihenfolge zusammensetzen.

Gut, also selektiere ich in einer Zeile das Datum und schreibe es verkehrt herum auf als Überschrift in eine Datei, weil nur so garantiert ist, dass die Batch nach Jahren,Monaten und Tagen (YYYYMMDD) in einer Reihenfolge existiert und lasse dann beim Zusammensetzen die "automatische Sortierfunktion" von Windows beim Zusammenfassen von Dateien wirksam werden: Windows übernimmt also freiwillig die Sortierung - Gedankengänge zu kompliziert oder nicht verstanden?

Dann folgt jetzt die Batch und ich erkläre dann die einzelnen Zeilen und dann sollten Sie die Batch und deren Möglichkeiten verstehen:

Die Batch heißt irgendeinname.bat

FOR /F "tokens=*" %%A IN ('findstr /N .* "vers.txt"') DO echo %%A>> vers.bak
FOR /F "tokens=1,2 delims=:"  %%i  IN (vers.bak) DO echo %%j>>%%i.bak
del vers.bak
set /a x=1
:weiter
set /P strang=<%x%.bak
set altstrang=%strang%
set strang=%strang:"=1%
set strang1=%strang:~6,4%
set neu1strang=%neu1strang%%strang1%
set strang=%strang:"=1%
set strang1=%strang:~3,2%
set neu1strang=%neu1strang%%strang1%
set strang=%strang:"=1%
set strang1=%strang:~0,2%
set neu1strang=%neu1strang%%strang1%
echo.%altstrang%>>%neu1strang%.tnt
set strang=""
set altstrang=""
set strang=""
set strang1=""
set neu1strang=""
set /a x=%x%+1
if exist %x%.bak (goto weiter) else goto endspurt
:endspurt
del *.bak
for /f "TOKENS=*" %%a in ('dir *.tnt /b') do type "%%a" >>ergebnis.txt
del *.tnt
 
---

Achtung: Die rote Zeile ist die einzige, die Sie anpassen müssen (vers.txt), es sei denn die CSV,TXT oder LOG-Datei, die Sie verwenden heißt vers.txt. Ansonsten werden alle BAK und TNT-Dateien relativ im selben Verzeichnis (Ordner) gelöscht, aber die gibt es nicht oder sind nicht mehr üblich.

Batch und die zweite Datei müssen im selben Ordner liegen (relative Bezüge)

Ganz ruhig, ist nicht schwer....

Jetzt einmal Zeile für Zeile:

FOR /F "tokens=*" %%A IN ('findstr /N .* "vers.txt"') DO echo %%A>> vers.bak

Der Versuchstext wird mit dieser Zeile einfach numeriert und in ein Dokument Namens Vers.bak umgeleitet, deshalb auch zwei Ausgabezeichen, weil der Text sonst immer wieder überschrieben werden würde (destruktives Überschreiben...)

Es steht also im Versuchsdokument mit der Endung BAK:

1: 25.07.1970 Manfred Thiele

In der zweiten Zeile

2: 21.12.2006 test

Und so weiter bis zur letzten Zeile. Es ist egal, wie viel Text da jetzt drin steht, nur ein Doppelpunkt würde später zu einer einzelnen Fehlausgabe führen...

Warum diese Aktion?

Ich will einfach den Text in Zeilen zerlegen, die später in einzelnen Dokumenten vorliegen, die ich dann bearbeite. Schon die nächste Zeile hilft weiter:

FOR /F "tokens=1,2 delims=:"  %%i  IN (vers.bak) DO echo %%j>>%%i.bak

In dieser Zeile wird der Text in zwei Teile zerlegt: Alles was vor dem Doppelpunkt steht ist die Variable %%i und was nach dem Doppelpunkt steht %%j, und der Doppelpunkt ist ein sogenannter Separator, der nicht ausgedruckt wird.

Bei Batches sind das immer zwei Prozentzeichen und die zwei Tokens - also Teile - sind vom Doppelpunkt getrennt. %%i auf der linken Seite ist immer die Nummer der Zeile.

Also bekomme ich im Ergebnis ein 1.bak,2.bak,3.bak...., ebenso viele bak-Dokumente wie Zeilen.

Hm, wäre auf der rechten %%J-Seite ein weiterer Doppelpunkt, dann würde nach dem zweiten Doppelpunkt der Text abgeschnitten werden.

Oder es gäbe ein %%k und man müsste das anpassen, in dem man nun 3 Tokens (1,2,3) und bei der Ausgabe ein %%k hinzufügt!!!

Das wäre auch nur sinnvoll, wenn in allen Zeilen es eben einen Doppelpunkt gibt.

del vers.bak

Ich lösche nun das Zwischendokument. BAK war übrigens eine real existierende Änderung, die man in alten Windowszeiten für Backup-Dateien verwendete. Später werden noch einmal *.tnt Dateien verwendet: Die gibt es nicht. Ist auch egal, Windows öffnet die mit dem Editor und dann steht immer Text drin: Ich will später keine Log oder Textdateien löschen müssen, weil das dann teilweise fatal wäre: Deshalb schaffe ich Dateien mit Endungen, die den üblichen nicht in den Weg kommen...

set /a x=1

Ich setze eine Variable X auf 1, die mit dem Schalter /a in Zukunft auch als Zahl behandelt wird, weil x im folgenden hochgezählt wird und zwar in der typischen Batchschleife:

:weiter

Weiter ist ein Selbst gewähltes Wort und der Doppelpunkt ein Sprungpunkt: Sie müssten also wissen: Das wir uns nun am Anfang einer Schleife befinden. X ist auf 1 gesetzt und nun kommt eine sich wiederholende Handlung, bis wir aus der Schleife ausbrechen:

set /P strang=<%x%.bak
set altstrang=%strang%
set strang=%strang:"=1%
set strang1=%strang:~6,4%
set neu1strang=%neu1strang%%strang1%
set strang=%strang:"=1%
set strang1=%strang:~3,2%
set neu1strang=%neu1strang%%strang1%
set strang=%strang:"=1%
set strang1=%strang:~0,2%
set neu1strang=%neu1strang%%strang1%
echo.%altstrang%>>%neu1strang%.tnt

Das ist also der sich wiederholende Block und deshalb schrieb ich den aus:

X war also auf eins gesetzt und im ersten Durchlauf steht da also, dass wir (set /P strang=<%x%.bak) wir die erste Zeile im Dokument 1.bak auslesen und in einer Variable durch den Schalter /P einlesen, also

25.07.1970 Manfred Thiele (wenn sie das nicht glauben, dann entfernen sie vorherige del-Befehle und setzen ein pause vor dem Beginn der Schleife - die Batch hält dann an und sie können das kontrollieren)

Die Variabale heißt jetzt strang, welches ein frei gewählter Name des Strings ist.

set altstrang=%strang%

Ich sichere jetzt diese Variable zusätzlich in der Variable altstrang, weil ich später die ganze Zeile in ein neu erschafftes Dokument bringen will, welches das umgedrehte Datum ohne Punkt beinhaltet und dieses neues Dokument hat später als Hommage an AC/DC die Endung *. TNT

Die nächsten drei Zeilen sind jetzt immer eine sich wiederholende Einheit:

set strang=%strang:"=1%
set strang1=%strang:~6,4%
set neu1strang=%neu1strang%%strang1%

Wir setzen des String also auf einen oder 100 % und greifen in der zweiten Zeile nach dem sechsten Zeichen von Links vier Zeichen ab: set strang1=%strang:~6,4%

Aha, wir betreiben eine Stringzerlegung und sagen dem Computer: Nach den ersten sechs Zeichen (25.07.) von links nehme bitte einmal die nächsten 4 Zeichen (1970 ) und schneide Sie aus:

Das fügen wir in der dritten Zeile in neu1strang zusammen: Set neu1strang ist erstmal nichts und daran wird strang1 angefügt. Ist ungewöhnlich, aber da die anderen folgenden Dreierblöcke genauso aussehen, mache ich das in diesem Schema auch in dieser Zeile - ist nicht falsch...


Nicht mehr oder weniger steht dann in strang1 und nach der Salami-Taktik schneiden wir dann den Monat aus:

set strang=%strang:"=1%
set strang1=%strang:~3,2%
set neu1strang=%neu1strang%%strang1%

Jetzt wird  vom 4. bis zum sechsten Zeichen abgeschnitten (07) und ich hänge dass an neu1strang an: %neu1strang% wurde im ersten Schritt als 1970 definiert und da wird jetzt 07 angehängt, also kommt da 1970+07=197007 in der dritten Zeile heraus.

Ja, die CMD betrachtet das als Zeichen und nicht als Nummer, fängt deshalb nicht an 1970 mit 07 zu addieren.

Im dritten Abschnitt wird zum bestehenden %neu1strang% dann noch der Tag angehängt:

set strang=%strang:"=1%
set strang1=%strang:~0,2%
set neu1strang=%neu1strang%%strang1%

Brauche ich eigentlich nicht weiter zu erklären, nur dass hier der Tag  als letztes  angehängt wurde.

echo.%altstrang%>>%neu1strang%.tnt

Mit diesem Befehl mit einem Punkt nach dem Echo wird jetzt also noch einmal der ursprüngliche String im altstrang aufgerufen (25.07.1970 Manfred Thiele ), der in einem Textdokument mit der endung *.tnt gespeichert wird:

Um genau zu sein in der Beispielszeile 19700725.tnt
Das Datum in dem Dokument bleibt also erhalten, lediglich in der Überschrift verwenden wir das umgedrehte Datum.


Ich gebe zu, dass war eine schwierige Geburt, aber wir haben das erreicht, was ich wollte:  Ein  Array-Schnipsel mit  einer Datumsnummer, die nun im Ordner relativ liegt  und ich greife  etwas vor: Bei jedem Durchlauf wird so ein Schnipsel erstellt und vom Betriebssystem automatisch sortiert! Das bedeutet, dass ich kurz vor dem Ziel stehe....

set strang=""
set altstrang=""
set strang=""
set strang1=""
set neu1strang=""

Ich setze die Variablen alle auf Null, eigentlich müsste ich das nur mit einer tun, aber das können sie gerne allein korregieren. Na, dann will ich nicht so sein: setneu1strang würde bei jedem Durchlauf weiter expandieren...Deshalb muss zumindest diese Variable gelöscht werden....

set /a x=%x%+1
if exist %x%.bak (goto weiter) else goto endspurt
:endspurt

Die drei Zeilen gehören alle Zusammen, weil sie der zweite Teil der Schleifenkonstruktion sind und den Ausstieg ermöglichen.

Erinnern Sie sich: Wir hatten vor der schleife X auf 1 gesetzt.

Dadurch konnten wir  einen Schnipsel mit 1.bak ansprechen, in dem unsere Zeile Code stand, die früher aus dem zusammengesetzten Dokument zerlegt war und an erster Position gestanden hatte.

Nach dem ersten Durchlauf haben wir durch Stringmanipulation nun die erste Zeile des Ursprungsdokuments zerlegt und in ein Dokument gebracht, welches das umgedrehte Datum als Überschrift hat und nun setzen wir X um eine Zahl höher (set /a x=%x%+1).

Existiert nun ein 2.bak-Dokument (also eine zweite Zeile im Ursprungsdokument: if exist %x%.bak), dann geht es weiter mit der nächsten Schleife (goto weiter), ansonsten (else goto endspurt), wenn im Beispiel kein weiteres 6.BAK vorliegt, dann verlasse die Schleife und gehe zum Punkt endspurt.

Ich muss also nicht wissen, wie viele Zeilen Code das Ursprungsdokument hatte: Ich weiß aber, dass jede Zeile durchgängig nummeriert wurde und ein Zahl.BAK- Dokument mit laufender Nummer dazu besteht. In unserem Beispiel trifft dann beim sechsten Dokument die CMD bei der Prüfung kein Dokument mehr an und verlässte die Schleife.

So, mal überlegen, jetzt haben wir also lauter Dokumente im Format YYYYMMDD.tnt.

Die sortiert Windows automatisch in die richtige Reihenfolge -:) Und zwar nach der laufenden Nummer.

del *.bak
for /f "TOKENS=*" %%a in ('dir *.tnt /b') do type "%%a" >>ergebnis.txt
del *.tnt

Die letzten drei Zeilen sind also easy:

Die BAK-Dokumente brauche ich nicht mehr und lösche diese entsprechend. (del *.bak)

Danach sage ich zu Windows: Such bitte mal alle TNT-Dokumente und schreibe deren Inhalt in einen Ergebnistext (for /f "TOKENS=*" %%a in ('dir *.tnt /b') do type "%%a" >>ergebnis.txt)

Windows denkt sich nun, dass das O. K. ist und im Dir - Befehl wird das schon alphabetisch bzw. in diesem Fall per Zahlen im Verzeichnis sortiert: Ich brauche da keine Sortierfunktion oder einen Sortierbefehl, weil Windows das automatisch tut und schön nach Nummern sortiert:

So gesehen ist das amerikanische umgekehrte Datum einfach computerfreundlicher, weil eine Sortierung von Jahr Monat Datum einfach eine sinnvolle Sortierung ist:

Da ist keine Verwechslung möglich, während ich beim 24.12.2017 eben beim Lesen bis zum Punkt nach dem Monat eigentlich immer noch nicht weiss, ob dieses Jahr gemeint ist.

Als Mensch habe ich die Übersicht und sehe das Datum als Ganzes, aber der Computer kann aus einem Text das erstmal nicht so erkennen, weill er sich von links nach rechts durcharbeitet:

Ja, ist dass für den Computer als Date-Variable vorgegeben ist das in Ordnung, aber in einem Text sind das erstmal Zeichen und kein Datum...

Da der Computer auch in diesem Fall von links nach rechts liest und dementsprechend sortiert, funktioniert eben auch eine Sortierung nur, wenn man nach Jahren, Monaten und Tagen sortiert, andersrum gibt das Chaos...

Wie dem auch sei: Im Ergebnis-Text steht nun das Ergebnis und das war es dann auch und es müssen nur noch die TNT-Dateien vernichtet werden, vorsichtig, ist explosiv-:)

Fazit

Die Batch ist nicht schwer und auf der Meta-Ebene wissen sie nun, wie man gleichförmige Dokumente mit Uhrzeiten, Datum oder nach Buchstaben sortiert:

Sie müssen einfach nur die Ursprungsbatch anpassen und bei Sortieren nach Buchstaben:

Den ersten Buchstaben selektieren (drei Zeilen der Stringmanipulation anpassen), dann diesen als Überschrift verwenden und hinterher die Dokumente wieder zusammenfügen.

Ach ja, ich würde die ersten drei Buchstaben selektieren, dann wird die Sortierung genauer. Keine Sorge, sollten mehrere Zeilen mit z. B. a anfangen, dann wird der Inhalt durch die doppelten Ausgabezeichen in das letzte Hilfsdokument geschrieben (echo.%altstrang%>>%neu1strang%.tnt).

Also ist Sortierung mit dieser Batch kein Problem, aber ob das bei mehreren hundert Zeilen dann nicht zu zeitaufwendig wir, müssen sie alleine testen. Das ist also der Lösungsansatz auf der Meta-Ebene für beliebig lange Texte ohne Doppelpunkte, die sie innerhalb von HTML,TXT,CSV oder Log-Dateien nach Datum, Uhrzeit oder alphabetisch Zeilen innerhalb eines Dokuments sortieren.


Impressum

 







Impressum
Datenschutz