Hallo zusammen,
kann mir mal jemand "aus dem Handgelenk heraus" einen kurzen
Text-Ersetzungs-Einzeiler zur Verfügung stellen? Das geht sicherlich mit
awk oder sed und einen bash-Einzeiler, aber ich möchte jetzt nicht erst
mal 1/2 Stunde rumfummeln ...
gegeben ist eine (ziemlich umfangreiche) Logdatei [logdatei.txt]. Diese
enthält schwer lesbare [*1] Logeinträge, welche ich bei Schlüsselworten
ergänzen möchte. Diese Schlüsselworte stehen in einer weiteren Datei
[ersetzungen.txt]
[*1] Die Logdatei enthält natürlich nicht so einen Quatschtext wie unten
im Beispiel aufgelistet. Es sind umfangreiche technische Informationen.
Die Schlüsselworte sind Funktionsaufrufe, die sind aber vom Namen her
relativ "kryptisch", und sollen durch besser lesbaren, und direkt ins
Auge springenden Text (daher die [*** . ***] Umhüllung) ersetzt werden.
Die Quelldatei liegt auf einer Linux Maschine vor, die Ersetzungen
könnten auf der Linux Maschine oder unter Windows10 passieren (reines
Windows, kein cygwin o.ä. drauf vorhanden)
Beispiel:
logdatei.txt
oh wie schön ist doch diese pqrz Wolke
der gvra Fluss ist recht lang
Hier steht was zur Farbe ogqa
ersetzungen.txt
"pqrz","blau"
"gvra","gelb"
"ogqa","grün"
logdatei_neu.txt
oh wie schön ist doch diese [*** blau ***] pqrz Wolke
der [*** gelb ***] gvra Fluss ist recht lang
Hier steht was zur Farbe [*** grün ***] ogqa
leider kann ich auf ersetzungen.txt nicht verzichten. es sind ca. 100
Einträge in dieser Datei, die sich auch noch dynamisch verändern
(ergänzen)
ausserdem soll ja der ursprüngliche Text, UND der neue Text, nachher
vorhanden sein, siehe oben im Beispiel logdatei_neu.txt
Wegstaben V. schrieb:> leider kann ich auf ersetzungen.txt nicht verzichten. es sind ca. 100> Einträge in dieser Datei, die sich auch noch dynamisch verändern> (ergänzen)
Dann nimm die Lösung von jobi.
> ausserdem soll ja der ursprüngliche Text, UND der neue Text, nachher> vorhanden sein, siehe oben im Beispiel logdatei_neu.txt
Wenn Du oben nochmal nachliest, steht da "> logdatei_neu.txt". Die alte
Datei bleibt also erhalten. sed verändert die ursprüngliche Datei nicht.
Deine ersetzungen.txt sollte dann eben so aussehen:
s/pqrz/[*** blau ***] &1/g
s/gvra/[*** gelb ***] &1/g
s/ogqa/[*** grün ***] &1/g
dann
sed -f ersetzungen.txt < logdatei.txt
oh wie schön ist doch diese [*** blau ***] pqrz1 Wolke
der [*** gelb ***] gvra1 Fluss ist recht lang
Hier steht was zur Farbe [*** grün ***] ogqa1
An dem Punkt wo man auf der shell zu Metaprogrammierung greift wäre
meine persönliche Obfuskations-Schmerzgrenze schon überschritten. Das
wär mir zu Meta wie man so schön sagt. Ich würde mir stattdessen völlig
direkt ein kleines Python-Script zusammenklöppeln das dann zwar aus
wesentlich mehr (kürzeren) Zeilen besteht, dafür aber extrem lesbar,
wartbar und selbsterklärend wäre.
Bernd K. schrieb:> Das wär mir zu Meta wie man so schön sagt.
Wenn man Regular Expressions versteht, erscheint Yalus Zeile doch sehr
einfach und genial.
Der erste sed-Aufruf baut aus der Ersetzungstabelle die
Ersetzungsbefehle für den zweiten sed zusammen. Fertig. Da ist nix
magisches dran.
Frank M. schrieb:> Bernd K. schrieb:>> Das wär mir zu Meta wie man so schön sagt.>> Wenn man Regular Expressions versteht, erscheint Yalus Zeile doch sehr> einfach und genial.
Längere Regexes sehen halt meist etwas kryptisch aus. Selbst ein Profi
muss sie Zeichen für Zeichen durchgehen, um zu erkennen, was da genau
geschieht. Wenn man das Ganze in Python schreibt, würde man
wahrscheinlich ebenfalls Regexes benutzen, um sich das Leben etwas zu
erleichtern, nur würde man nicht versuchen, alles in eine Zeile zu
quetschen. Aber auch in einem Shell-Skript steht es einem frei, den Code
auf mehrere Zeilen zu verteilen bspw. so:
1
# Regex für die Konvertierung der Ersetzungsliste in sed-Kommandos
2
search='"\(.*\)","\(.*\)"'
3
replace='s/\1/\[*** \2 ***] \1/g'
4
ers2sed="s|$search|$replace|g"
5
6
sed "$ers2sed" ersetzungen.txt | # Konvertieren der Erstzungsliste
7
sed -f - logdatei.txt > logdatei_neu.txt # Ausführen der sed-Kommandos
Damit wird die Regex in zwei logisch sinnvolle Teile zerlegt und vom
weniger kryptischen Rest getrennt sowie jedem der beiden sed-Kommandos
eine eigene Zeile zugestanden. Natürlich sind auch Kommentare selten ein
Fehler.
> sed 's|"\(.*\)","\(.*\)"|s/\1/\[*** \2 ***] \1/g|' ersetzungen.txt | sed
2
> -f - logdatei.txt > logdatei_neu.txt
3
>
Anstelle von ANFÜHRUNGZEICHEN - BELIEBIGE ZEICHEN - SCHLUSSZEICHEN
vereinfache ich jeweils auf ANFÜHRUNGSZEICHEN - ALLES AUSSER
SCHLUSSZEICHEN - SCHLUSSZEICHEN.
Lieber wäre mir hier der ERE/PCRE Quantifier + (mindesten ein...)
anstelle von * (kein oder mehrere...), aber ich war bisher zu faul
herauszufinden ob möglich und wie man das bei sed einsetzt <:-)
Hans schrieb:> Möchtest du mit deinen Ersetzungen etwa Wegstaben verbuchseln?
nein, es sind Logeinträge "in etwa" wie diese:
12:23:14 Info call sub_chk3
das soll dann werden zu
12:23:14 Info call [*** Checkroutine zur Füllstandsüberprüfung ***]
sub_chk3
Frank M. schrieb:> Da ist nix> magisches dran.
Kryptische Shell Befehle sind eine Sache, aber kryptische Shell-Befehle
die kryptische Shell-Befehle generieren und dann ausführen ist mir
persönlich halt eine Stufe zu Meta. Aber das scheint wohl
Geschmackssache zu sein, jeder hat ne andere Definition von schönem
Code.
Bernd K. schrieb:> Kryptische Shell Befehle sind eine Sache, aber kryptische Shell-Befehle> die kryptische Shell-Befehle generieren [...]
Es werden keine Shell-Befehle generiert, lediglich reguläre Ausdrücke
für sed.
> Aber das scheint wohl> Geschmackssache zu sein, jeder hat ne andere Definition von schönem> Code.
Schreibe es doch mal klassisch auf, z.B. in schnödem C. Dann kann man ja
nochmal vergleichen, was "schöner" ist ;-)
Bernd K. schrieb:> ist mir persönlich halt eine Stufe zu Meta.
Das verstehe ich. Wenn man sich aber daran gewöhnt, sind die Vorteile
immens:
Die beiden Schritte sind jeweils einfach und (durch das eigenständige
Zwischenergebnis) nachvollziehbar. Wenn es hakt, sieht man sofort, in
welchem Schritt.
Die Adaption an andere Ersetzungs-files ist genauso einfach wie an
andere Ausgabenformate. Jeder Schritt kann einzeln probiert, verändert
werden.
Du brauchst keine aufwendige Compiler-collection. Als Debugger reichen
Pause und Echo.
Wenn Du ein monolithisches Script draus machst, dann scheint es
einfacher, weil die Komplexität von unnötigem trivialen Ballast umgeben
ist. Das größere Problem ist aber, dass das zwischenergebnis quasi nur
virtuell vorliegt und deshalb Debugger, Datenstrukturen, Architektur
notwendig werden.
Frank M. schrieb:> Schreibe es doch mal klassisch auf, z.B. in schnödem C.
C würd ich nicht nehmen sondern eher irgendeine Scriptsprache die eh
schon installiert ist weil sie zufällig für anderes auch genutzt wird.
A. S. schrieb:> Das größere Problem ist aber, dass das zwischenergebnis quasi nur> virtuell vorliegt und deshalb Debugger, Datenstrukturen, Architektur> notwendig werden.
In dem Fall des Einzeilers ist das Zwischenergebnis nur "virtuell"
vorhanden (in der Pipe), meinst Du das?
Um das Zwischenergebnis zu begutachten muss ich den Befehl zerlegen. Im
Script kann ich mir das mit nem print() schnell ausgeben lassen,
Debugger muss ich dafür nicht gleich auffahren.
Aber ich will da auch jetzt nicht groß drauf rum reiten, ich wollte nur
meine persönliche Meinung kundtun, ich finde die Syntax häßlich und auch
wenn es unbestritten von der Idee her elegant ist ist es letzten Endes
keineswegs schön, zumindest nicht in meinen Augen.
irgendie klappts doch nicht mit dem ersten "Search" Teil des sed
Kommandos.
Ok, meine Anforderungen haben sich ein wenig verändert, ich hatte
gehofft, das ich das mit kleinen Anpassungen selbst hinbekomme.
Zuer Erläuterung: die ersetzungen.txt kommt als Export aus Excel als csv
Datei. "c" heisst bei Excel aber Semikolon. Und auch die Gänsefüsschen
zur Umklammerung der einzelnen Felder gibt es nicht. Irgendwelche
Makros, welche das in excel integrieren, sollen nicht eingesetzt werden.
Darüber hinaus gibt es nicht 2, sondern 3 Spalten, und manchmal ist
Spalte 2 bzw. Spalte 3 nicht belegt.
Ich hatte jetzt ich die ursprüngliche "Spezifikation" genommen, die
ersetzungen.txt csv Datei entsprechend angepasst. Alle
nicht-3-spalten-Einträge wurden rausgelöscht, das Semikolon als
Feldtrenner durch Komma ersetzt, udn die einzelnen Felder mit
Gänsefüsschen geklammert.
Ein Zeileneintrag sieht nun so aus:
"Checkroutine zur Füllstandsüberprüfung","Subroutine","sub_chk3"
der Aufruf von
sed 's|"\(.*\)","\(.*\)","\(.*\)"|s/\1/\[*** \3 ***] \1/g|'
ersetzungen.csv | sed -f - quelldatei > zieldatei
liefert folgenden Fehler:
sed: file - line 2: unknown command: `"'
Wie sieht die Zeile 2 von ersetzungen.csv aus?
Entweder weicht dort die Syntax von dieser Vorgabe
"Checkroutine zur Füllstandsüberprüfung","Subroutine","sub_chk3"
ab, oder die Strings enthalten böse Sonderzeichen. Ich bin bei meinem
obigen Lösungsvorschlag der Einfachheit halber davon ausgegangen, dass
die Strings keine Sonderzeichen enthalten und habe deswegen auf eine
spezielle Behandlung derselben verzichtet.
Das ursprüngliche Excel-CSV-Format mit den fehlenden Anführungszeichen
und dem Semikolon statt des Kommas könnte mit angepassten Regexes sicher
auch von sed verarbeitet werden.
Yalu X. schrieb:> Wie sieht die Zeile 2 von ersetzungen.csv aus?
hm, vermutlich ein cr / crlf Problem (ich hatte das "echte" Quellobjekt
mittels winscp vom WIn10 Rechner auf die Linux Maschine übertragen, aber
dabei den Binärmodus eingestellt gehabt.
Jetzt hab ich mal direkt auf der Linux Maschine Dateien angelegt.
Es kommt jetzt kein Fehler (mehr), es wird aber auch nix ersetzt.
ersetzungen-dummy.txt
"Ein wundervolles ersetztes erstes Objekt","hurz","asdf"
"auch was schönes","bubu","jklö"
quelldatei.txt
sdfsf sdfsdf sdfs dfsdf sdfsdfsdf sdfsdf sdf
jdjdjdjjd f hurz sdfsdf sfsf sdf sdfsf sdf sdf sf
asdad asdf sdf sdf sdfsf sdf sdf sdfsf
asdad fsdfsf jklö
Befehl
sed 's|"\(.*\)","\(.*\)","\(.*\)"|s/\1/\[*** \3 ***] \1/g|'
ersetzungen-dummy.txt | sed -f - quelldatei.txt > zieldatei.txt
zieldatei.txt
sdfsf sdfsdf sdfs dfsdf sdfsdfsdf sdfsdf sdf
jdjdjdjjd f hurz sdfsdf sfsf sdf sdfsf sdf sdf sf
asdad asdf sdf sdf sdfsf sdf sdf sdfsf
asdad fsdfsf jklö
Wegstaben V. schrieb:> Es kommt jetzt kein Fehler (mehr), es wird aber auch nix ersetzt.
Logisch, denn es wird nach "Ein wundervolles ersetztes erstes Objekt"
und "auch was schönes" gesucht, was beides in quelldatei.txt nicht
vorkommt.
Du möchtest wahrscheinlich die \1 und \3 im sed-Kommando tauschen. Dann
sieht das Ergebnis so aus:
1
sdfsf sdfsdf sdfs dfsdf sdfsdfsdf sdfsdf sdf
2
jdjdjdjjd f hurz sdfsdf sfsf sdf sdfsf sdf sdf sf
3
asdad [*** Ein wundervolles ersetztes erstes Objekt ***] asdf sdf sdf sdfsf sdf sdf sdfsf
4
asdad fsdfsf [*** auch was schönes ***] jklö
Die zusätzlichen CRs des DOS-Dateiformats stören bei mir weder in
ersetzungen-dummy.txt noch in quelldatei.txt (sed 4.7 unter Linux).
Bernd K. schrieb:> meinst Du das?
Oh, sorry, nein.
Ich mach es dann wirklich in 2 (bzw. n) Stufen.
Der Einzeiler macht aus der .txt eine batchdatei, die macht dann die
Ersetzung (und ggf so weiter). Kommt make auch gut mit klar, und
notfalls kann man die batchdatei manuell frisieren, justieren, um neues
auszuprobieren bis es klappt und danach den Einzeiler anpassen.
Also eher "one Task and ... well".
Yalu X. schrieb:> Dann sieht das Ergebnis so aus:
(richtiges Ergebnis)
Juhu! Genau so soll es sein. Jetzt schau ich mal, das ich das heraus
bekomme, warum die echte Datei rumzickt, und mache die Anpassungen der
"neuen Spezifikation".
Vorher aber: Prost! Jetzt trinke ich erst mal ein leckeres Kölsch auf
dein Wohl!
so, das Kölsch hat geholfen.
Die Ursache, warum die Original ersetzungen.txt Datei nicht ging, ist
identifiziert: Die berühmte Umlaut-Falle unter Linux hat mal wieder
zugeschlagen.
z.B. das Wort 'für' (es kommt in meiner Original Datei vor) :
Original (als Windows Datei, per winscp auf die Linux Maschine gebracht)
66 fc 72
auf der Linux Maschine erstellte ersetzungen-dummy Datei mit dem Wort
'für'
66 c3 bc 72
der genutzte Editor (xed auf der Linux Maschine) stellt "fieserweise"
auf der GUI beides als 'ü' dar. Im Terminal-Fenster auf Linux sieht man
jedoch das zerknödelte 'ü' der 8859 Codierung.
mit ein bischen Mouse-Over auf dem jeweiligen Tab blendet sich dann
sogar beim xed die Zusatzinfo ein:
Zeichencodierung: Westlich (ISO-8859-15) -> Windows Datei
Zeichenkodierung: Unicode (UTF8) -> Linux Datei
-> sed kommt da wohl nicht zurecht. Ich hab mir da jetzt was mit iconv
zurecht gefummelt.
Übrigens kam ich irgendwie nicht zurecht mit dem CR aus der Windows
Datei, die musste ich auch erst mal eliminieren. Meine momentane
funktionierende Lösung ist also:
cat ersetzungen.txt | tr -d '\r' > ersetzungen_cr.txt
iconv --from-code=ISO-8859-15 --to-code=UTF-8 ersetzungen_cr.txt >
ersetzungen_neu.txt
sed 's|\(.*\);\(.*\);\(.*\)|s/\3/\[*** \1 = \2 ***] \3/g|'
ersetzungen_neu.txt | sed -f - quelldatei.txt > zieldatei.txt
... und die ersetzungen.txt muss vollständig sein, das heisst: alle 3
Spalten müssen befüllt sein