• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

sed zeilenende LF oder CR

Moin Moin,

ich hätte da mal wieder ein Problem bei dem ich nicht weiter komme. Ich habe hier eine Datei als stderr von ffmpeg. In dieser Datei wechselt das Zeilenende von LF zu CR und wieder zurück. Nun wollte ich aus den Zeilen die auf CR enden etwas heraus schneiden bzw löschen. Klappt aber nicht.
Von daher wäre es praktisch wenn ich sed mitteilen könnte CR ebenfalls als Zeilenende zu akzeptieren. ixquick liefert mir da aber auf die Schnelle keinen Hinweis. Hat einer von euch eine Idee wie man das machen könnte?
 

TomcatMJ

Guru
Vielleicht klappts mit dem Billig-Trick über die Konvertierung mit unix2dos womit aus CR einfach CR/LF wird und dann erst sed drauf ansetzen?
 
OP
Geier0815

Geier0815

Guru
Das ist eher weniger geeignet da das Ganze später wahrscheinlich "on the fly" passieren soll, also die Fehlerausgabe von ffmpeg direkt in eine pipe geleitet. Einen evtl dirty hack hab ich jetzt doch gefunden:
Code:
tr '\r\' '\n' < DATEI | sed 's/regex//'
Empfinde ich als Krücke auch im Hinblick darauf das das CR evtl erhalten bleiben sollte.
 

abgdf

Guru
In Perl kann man den $INPUT_RECORD_SEPARATOR neu definieren, z.B.:
Code:
$/ = "\n"; # LF
$/ = "\r"; # CR
Regex in Perl ist sed auch sehr ähnlich.
 
OP
Geier0815

Geier0815

Guru
Ich könnte es über den InputFieldSeperator machen aber dann ist das Ganze nicht so sonderlich tauglich für eine pipe. Irgendwie bin ich damit auch nicht glücklich.
Code:
#!/bin/bash
IFS=$'\r\n'
for zeile in `cat ./DATEI`
do
echo $zeile | sed 's/regex//' >> ./OUT
done
IFS direkt in sed wäre praktisch, scheint es aber nicht zu geben oder ich hab es noch nicht gefunden.
 

abgdf

Guru
Wenn Du die "\r" wirklich erhalten willst, könnte vielleicht so ein Skript anstelle Deines sed in der "echo ... | sed ..."-Zeile das tun:
Code:
#!/usr/bin/perl

while (<>) {
    my $a = $_;
    if ($a =~ /\r/) {
        my @b = split("\r", $a);
        for my $u (0 .. $#b - 1) {
            $b[$u] =~ s/regex//;
        }
        my $c = join("\r", @b);
        print $c;
    }
    else {
        print $a;
    }
}
Die CR platt machen ginge noch leichter, beschrieben hier.
Code:
open(my $in, '<:crlf', $filename);
liest offenbar beide in einer Datei, konvertiert aber automatisch zu LF.
 
OP
Geier0815

Geier0815

Guru
Ok, meine zweite Lösung macht letztlich genau das gleiche wie die Erste, sprich auch da werden die CR entfernt und durch LF ersetzt. Ist letztlich, wenn man drüber nachdenkt wie sed arbeitet, ja auch klar: sed entfernt beim Reinkommen das newline (oder blendet es aus) einer jeden Zeile und hängt es an jede Rausgehende wieder dran. So langsam kommt mir der Verdacht das sed in diesem Fall nicht die richtige Wahl ist.

Wahrscheinlich schlägt robi hier nachher auf und lacht uns aus und zeigt uns dann einen ganz einfachen Weg per Befehl oder Option enes Befehls /dieden wir noch nicht kannten... :schockiert:
 

TomcatMJ

Guru
Würde mich auch nicht wundern wenn dann jemand mit AWK kommt (von dem ich bis dato erfolgreich die Finger lassen konnte*G*)...
 

abgdf

Guru
Warum willst Du überhaupt manchmal LF, manchmal CR, in einer Datei oder einem Output beibehalten?
Das kann dann ja keines der Systeme (Linux: LF, MacOS9: CR) verarbeiten.
 
A

Anonymous

Gast
Geier0815 schrieb:
Wahrscheinlich schlägt robi hier nachher auf und lacht uns aus und zeigt uns dann einen ganz einfachen Weg per Befehl oder Option enes Befehls /dieden wir noch nicht kannten... :schockiert:
mit sed geht sowas wirklich ist aber "Gewurschtel" da man wahrscheinlich über Multilinebearbeitung gehen muss. Das wird wunderbar kryptisch, da blickt niemand durch um sowas dann später für seine eigenen Aufgaben abzuwandeln.
TomcatMJ schrieb:
Würde mich auch nicht wundern wenn dann jemand mit AWK kommt (von dem ich bis dato erfolgreich die Finger lassen konnte*G*)...
hier sind wir Richtig. ;) ;) ;) Unter der Voraussetzung das wir bei der Bearbeitung das \r und das \n an den entsprechenden Stellen stehen lassen wollen, geht das im Prinzip so hier.

Erstmal als NULL-Bearbeitung
Code:
awk 'BEGIN{RS="[\r\n]"};{ ORS=RT;print $0 }'  datei

Das Prinzip ist folgendes: mit der Variable RS definieren wir beides als möglichen Eingabe-Zeilentrenner.
In jeder Zeile steht dann jeweils in der Variable RT welcher der beiden Trenner diese Zeile hatte.
Wir setzten jetzt die Variable ORS die den Ausgabezeilentrenner bestimmt, auf den jeweiligen aktuellen Wert von RT , und printen.
und fertig ist der Kuchen. ;)

Nun sollen wir nur die Zeilen bearbeiten die auf \r enden und zwar wollen wir jedes vorkommen von "HALLO" zu "ECHO" ändern, und zwar nur in den Zeilen die auf \r enden. Wir können also pro Zeile entweder die Variable RT oder ORS auswerten um zu sehen welchen Zeilentrenner diese Zeile hatte (und auch wiederbekommt)
Code:
awk 'BEGIN{RS="[\r\n]"};{ ORS=RT;if (ORS=="\r") { $0 = gensub("HALLO","ECHO","g")}; print }' datei
das Austauschen ist hier ein bisschen komplizierter als in sed, hier wurde jetzt mal das universelle "gensub" verwendet, das lässt sich auf fast alle Varianten anwenden und zeigt die prinzipielle Benutzung.

Funktionen die hier zB möglich sind.
gensub(r,s[,h[,t]]) Ersetzt r in t durch s, gibt Ergebnis zurück; h="g/G"(global) oder Zahl(=Position), sonst nur 1. Position; t bleibt unver ändert, ohne t wird $0 verwendet; () ist in r und \1-\9 ist in s verwendbar

gsub(r,s[,t]) Ersetzt überall in t durch s; gibt Anzahl Ersetzungen zurück ohne t wird $0 verwendet [global]

sub(r,s[,t]) Wie gsub, ersetzt aber nur die erste Teilkette
Beachten muss man hier die unterschiedlichen Handhabung von gensub und gsub/sub.
gensub verändert t nicht. und hat die Änderung im Rückgabewert, weshalb wir die oben $0 wieder zuweisen müssen.

gsub gibt die Anzahl der Ersetzungen zurück und ändert t .
also würde die gesamte Befelszeile hier noch um einiges einfacher
Code:
awk 'BEGIN{ RS="[\r\n]" };{ ORS=RT; if (ORS=="\r"){ gsub("HALLO","ECHO")};print }'

und wenn man mal spaßeshalber hinten an den Befehl noch " | hexdump -C" anhängt, wird man erkennen, da ist \n und \r und auch "\n\r" oder "\r\n" noch genauso dasteht, wie in der Ausgangsdatei. ;)

Seltsamerweise bricht man sich bei Problemen mit dem Zeilemsprung beim Programmieren in vielen Scriptsprachen die Finger, außer in awk. ;)

robi
 

TomcatMJ

Guru
Ha!Ich wusste es doch!Was sed nicht oder nur mit Hampelei kann,kann awk und andersherum..und wie ich vermutete weiss unser defintiver Shell-Guru auch da die Lösung :D :thumbs:
 
A

Anonymous

Gast
TomcatMJ schrieb:
..und wie ich vermutete weiss unser defintiver Shell-Guru auch da die Lösung :D :thumbs:
Mal so auf die Schnelle, da kommen dann solche Dinger hier dabei raus. Aber passt noch lange nicht wirklich richtig auf ALLES mögliche. Nur meine einfach erzeugte Testdatei bei der sich die Zeilenumbrüche immer Zeileweise abwechseln macht es zufällig Richtig.
Eine einigermaßen Lösung bei der \r und \n nicht zerstört wird hätte wahrscheinlich noch ein dutzend Sonderzeichen und Buchstaben mehr. ;-)
Code:
sed -n 'tB;:A;s/^\(.*\)HALLO\(.*\)\r\(.*\)$/\1ECHO\2\r\3/;p;d;N;:B;y/\r/\r/;tA;p;d;N;bB'  datei
Aber mit solchen Dingern braucht man hier im Forum nicht zu kommen, die kann sowieso keiner wirklich gebrauchen und es dauert in der Entwicklung einer SED-Lösung auch noch ein vielfaches der Zeit einer AWK-Lösung.
Anderer Lösungssansatz über eine Pipe zuerst das "\r" gegen zB gegen "~\n" austauschen, dann nur die Zeilen mit sed bearbeiten die am Ende ein "~" haben und dann mit einem dritten Befehl in der Pipe das "~\n" wieder in "\r" umwandeln.

robi
 
OP
Geier0815

Geier0815

Guru
abgdf schrieb:
Warum willst Du überhaupt manchmal LF, manchmal CR, in einer Datei oder einem Output beibehalten?
Das kann dann ja keines der Systeme (Linux: LF, MacOS9: CR) verarbeiten.
Wenn ffmpeg arbeitet, hast Du als Ausgabe, neben der neuen Datei, auch Ausgaben auf stderr die dir am Bildschirm angezeigt werden. Normale Ausgaben werden zeilenweise mit LF beendet und damit normal ausgegeben. Aber dann hast Du sicherlich schon diese "Fortschrittsanzeigen" der Art: "frame bla dies das jenes" gesehen die nicht nacheinander sondern an einer Stelle laufen. Gibt es auch in anderen Programmen. Diese werden durch das CR erzeugt. Schreib dir mal eine Text-Datei die von 1 bis 100 geht und ersetze die LF durch CR, zB mit Scite achte darauf das am Anfang ein LF steht und hinter der 100 ein CR LF. Dann laß die Datei per cat ausgeben und Du siehst als Ausgabe nur die 100 weil das Hochzählen zu schnell läuft. Könntest Du cat verlangsamen würdest Du die Zahlen der Reihe nach immer auf der gleichen Zeile am Anfang sehen. CR bedeutet ja carriage return, also Wagenrücklauf und kommt noch aus den Zeiten der mechanischen Schreibmaschinen. Unser newline wie wir es kennen ist eigentlich ein LF/CR also Zeilenumbruch und Wagenrücklauf obwohl es nur noch als LF dargestellt wird, die andere Form gehört zu MSDOS. CR funktioniert unter Linux also exakt so wie es gemeint ist.
Wenn man, wie ich, solch eine Ausgabe "on the fly" ändern möchte, dann kommen solche Fragen dabei raus.

@robi,
wie immer bleibt mir nur mich ganz tief zu verneigen und festzustellen das ich nichts weiß. Ich frage mich echt wie viele Leben man haben muß um solch "simple, altmodische" Sachen wie sed und awk wirklich zu verstehen. Und auch nicht zum ersten Mal denke ich das Du ein Buch zu diesen Themen schreiben solltest. Soviel detaillierte Erklärungen, mit den ich echt etwas anfangen kann, lese ich sonst von niemandem.
 
OP
Geier0815

Geier0815

Guru
robi schrieb:
Code:
sed -n 'tB;:A;s/^\(.*\)HALLO\(.*\)\r\(.*\)$/\1ECHO\2\r\3/;p;d;N;:B;y/\r/\r/;tA;p;d;N;bB'  datei
Aber mit solchen Dingern braucht man hier im Forum nicht zu kommen, die kann sowieso keiner wirklich gebrauchen und es dauert in der Entwicklung einer SED-Lösung auch noch ein vielfaches der Zeit einer AWK-Lösung.
Anderer Lösungssansatz über eine Pipe zuerst das "\r" gegen zB gegen "~\n" austauschen, dann nur die Zeilen mit sed bearbeiten die am Ende ein "~" haben und dann mit einem dritten Befehl in der Pipe das "~\n" wieder in "\r" umwandeln.

robi
Ok, da ist dann ganz vorbei... Ich erkenne da irgendwo eine Sprungmarke am Anfang (aber auch nur weil ich darüber bei meiner Suche gestolpert bin), eine Substitution die ein "ich versuch alle Möglichen und unmöglichen Zeichen zu erkennen und später so wieder aus zu geben" beinhaltet und dann ehrlich gesagt gar nichts mehr. Danke, jetzt fühl ich mich so richtig doof :D
Der zweite Ansatz wäre dann über ein
Code:
tr '\r' '~n' | sed 's/HALLO/ECHO/p' | tr '~n' '\r'
? Auch eine gute Idee auf die ich so wohl nicht gekommen wäre.
 

abgdf

Guru
Geier0815 schrieb:
Aber dann hast Du sicherlich schon diese "Fortschrittsanzeigen" der Art: "frame bla dies das jenes" gesehen die nicht nacheinander sondern an einer Stelle laufen. Gibt es auch in anderen Programmen. Diese werden durch das CR erzeugt. Schreib dir mal eine Text-Datei die von 1 bis 100 geht und ersetze die LF durch CR, zB mit Scite achte darauf das am Anfang ein LF steht und hinter der 100 ein CR LF. Dann laß die Datei per cat ausgeben und Du siehst als Ausgabe nur die 100 weil das Hochzählen zu schnell läuft.
Ok, Du meinst also:
Code:
perl -e '$|=1;for(1000..1010){print "$_\r";sleep 1;}'
Na ja, mein kleines Skript oben dürfte auch funktionieren. Hat das mal jemand probiert?
Aber man hätte einen Aufruf von Perl oder von mir aus auch von awk pro Zeile. Das ist ja nun nicht optimal (robi meckert ja immer über Overhead schon bei nur einem Perl-Aufruf, da sind 10.000 awk-Aufrufe auch nicht viel besser). Entweder man schriebe ein erheblich kleineres Programm in C.

Oder besser, man kommuniziert gleich richtig mit ffmpeg.
In Perl nennt man das IPC, in Python benutzt man das "subprocess"-Modul.
Es gibt auch allerhand Perl-Module, die direkt auf die Kommunikation mit ffmpeg spezialisiert sind.

(Ich hab' mal z.B. mit so einem Perl-Spezialmodul für mpg123, das mir gut gefallen hat, ein Steuerskript für mp3s geschrieben (shufflemp3.pl, hier).
To run it, two Perl-modules are needed: First, there's "Term::Readkey", which can be compiled with:

tar -xzvf TermReadKey-2.31.tar.gz
cd TermReadKey-2.31
perl Makefile.PL
make
make install

Then there's "Audio::play::-MPG123", which can be compiled with:

tar -xzvf Audio-Play-MPG123-0.63.tar.gz
cd Audio-Play-MPG123-0.63
perl Makefile.PL
make
make install
)
 
Oben