Hallo!
Ich stehe vor folgendem Problem: Am seriellen Port kommt ein Datenstring
rein, der mit <CR><LF> am Ende abgeschlossen ist.
Dieser String soll in eine Datei geschrieben werden. Mit diesem Script
funktioniert es auch, allerdings nicht so, wie ich es gerne hätte:
1
while true
2
do
3
read LINE < /dev/ttyS0
4
echo $LINE >> datei.txt
5
echo $LINE
6
done
Die Daten werden zwar aufgezeichnet, allerdings immer mit dem <CR><LF>
am Ende, was zur Folge hat, das in der Datei nach jedem String eine
Leerzeile entsteht.
Es müsste folgendes realisiert werden:
Daten werden empfangen, wenn <CR><LF> empfangen wird, wird der String
gespeichert, allerdings ohne <CR><LF> am Ende. Jetzt wird eine neue
Zeile angefangen und der nächste String gespeichert.
Vielleicht kann mir jemand helfen. Ich komme da einfach nicht weiter.
Danke schonmal.
Kilian
Ich würde mal ein
stty -F /dev/ttyS0 9600 raw (bzw. entsprechende Baudrate)
ausführen, damit eventuelle CR nicht in zusätzliche CR-NL übersetzt
werden.
Du möchtest also den Zeilenumbruch entfernen und dafür einen
Zeilenumbruch einfügen?
Malte S. schrieb:> Was Jürgen sagt und ansonsten könnte | tr -d '\r\n' Dein Freund sein.> socat kann auch \r\n <=> \n umsetzen etc.
oder auch einfach dos2unix.
echo $LINE ist suboptimal, da nicht 1:1 transparent. Damit werden zum
Beispiel führende Leerzeichen unterdrückt.
Wenn schon, dann
echo "$LINE"
mit Anführungszeichen, damit Whitespaces erhalten bleiben.
Aber warum das Zeugs überhaupt in einer Shell-Variablen
zwischenspeichern?
Und echo fügt doch den Zeilenumbruch wieder ein? Okay, es fügt ein
simples '\n' statt einem "\r\n" ein, also einen UNIX-Zeilenumbruch.
Die einfachere Lösung ist:
man tee
Dann bleibt übrig:
IRGENDEIN-FILTER-WIE-TR < /dev/ttyS0 | tee datei.txt
Also z.B.
tr -d '\r' < /dev/ttyS0 | tee datei.txt
Der tr schmeisst also das CR ('\r') raus und lässt das NL ('\n') als
Unix-Zeilentrenner drin. Der tee-Aufruf speichert die Zeichen in der
Datei und gibt sie zusätzlich noch auf der Console aus.
Oder wenn man dos2unix hat:
dos2unix < /dev/ttyS0 | tee datei.txt
Was will man mehr? Simpler gehts nicht. Eine While-Schleife, die das TTY
und die Datei immer wieder neu öffnet, ist einfach Verschwendung.
P.S.
Wenn schon eine while-Schleife, dann:
while true
read $LINE # Lesen aus TTY
IRGENDEIN-FILTER
echo "$LINE" # Ausgabe in Datei
echo "$LINE" >&2 # Ausgabe auf Console
done </dev/ttyS0 >datei.txt
Dann werden beide Dateien nur einmal geöffnet. Aber wie gesagt: Braucht
man hier nicht, das wäre wie mit Kanonen auf Spatzen zu schießen. Die
UNIX-Kommandos sind vielfältig und in Ihrer Kombination ein mächtiges
Werkzeug. Leider beherrschen es immer weniger Leute richtig.
Ich hatte mal solch ein Konstrukt:
cat <file> | while read line
# tail -f -n 0 <file> | while read line
do
echo "$line" > <outfile>
done
(entweder die cat, oder die tail-Zeile aktivieren, wobei das tail hier
nicht unbedingt speziellen Sinn macht - ist halt aus einem meiner
Scripte, wo es nötig war).
Ob das auch zusätzliche Breaks erzeugt bei der seriellen Schnittstelle,
weis ich nicht. Aber bei normalen Inputfiles jedenfalls nicht.
Jens G. schrieb:> Ich hatte mal solch ein Konstrukt:>> cat <file> | while read line
Useless use of cat. Der Prozess ist hyperfluid.
Ein
while read line .... done <file
macht dasselbe.
> # tail -f -n 0 <file> | while read line> do> echo "$line" > <outfile>> done>> (entweder die cat, oder die tail-Zeile aktivieren,
Deine while-Schleife mit cat macht exakt dasselbe wie ein
dd if=infile of=outfile
oder
cat infile >outfile
oder
cp infile outfile
berücksichtigt also 2 Anforderungen des TOs nicht:
1. Umwandlung von \r\n nach \n
2. Zusätzliche Ausgabe auf der Console
Erklär mir bitte mal, warum Du so etwas wie da oben "in einem Deiner
Scripte" benötigtest.
@Frank M. (ukw)
Eigentlich sollte das ja nur ein Denkanstoß sein, falls es von Vorteil
sein sollte. Wenn Du meinst, daß es anders besser geht, dann ist auch
gut.
Klar, die cat-Variante kopiert letztendlich nur ein File. Dieses File
sollte aber eben /dev/ttyS0 sein in des TOs Aufgabe.
Auch war mir anfangs nicht ganz klar, woher das zweite crlf herkam
(auser von den Ausgangsdaten) - inzwischen ist mir jetzt klar, daß jedes
Echo da einen crlf reinmacht (da hatte ich anfangs nicht dran gedacht).
Aber um es Dir zu erklären: in meinem Scripts benutze ich die
tail-Variante, nicht die cat-Variante (um bestimmte Dateien nach
bestimmten neuen Strings permanent zu filtern, und bestimmte Aktionen
auslösen zu können).
Das mit dem cat hatte ich jetzt nur schnell mal eingebaut für diesen
thread als Anregung, was aber nun offensichtlich keinen Sinn mehr macht.
Jens G. schrieb:> Eigentlich sollte das ja nur ein Denkanstoß sein, falls es von Vorteil> sein sollte. Wenn Du meinst, daß es anders besser geht, dann ist auch> gut.
Ist schon okay. Ich wollte nur damit ausdrücken, dass man auf cat in 99%
aller Fälle verzichten kann. Der Ausdruck "Useless use of cat" ist dafür
ein gängiges Stichwort in den einschlägigen Kreisen. Google sollte Dir
da viele tausend Treffer zeigen.
> Klar, die cat-Variante kopiert letztendlich nur ein File. Dieses File> sollte aber eben /dev/ttyS0 sein in des TOs Aufgabe.
Ja, aber auch beim /dev/ttyS0 bleibt das cat überflüssig. Auch hier geht
Redirection. Unter UNIX/Linux bleibt eine Datei eine Datei. Egal, ob
sich eine reale Datei im Filesystem oder ein Gerät dahinter verbirgt.
> Auch war mir anfangs nicht ganz klar, woher das zweite crlf herkam> (auser von den Ausgangsdaten) - inzwischen ist mir jetzt klar, daß jedes> Echo da einen crlf reinmacht (da hatte ich anfangs nicht dran gedacht).
Falsch: Ein echo erzeugt unter UNIX nur einen LF, aber kein CR. Der TO
bekommt das CRLF-Paar von der seriellen Schnittstelle und will das CR
nicht in der Protokoll-Datei, weil das eben UNIX-unüblich ist.
> Aber um es Dir zu erklären: in meinem Scripts benutze ich die> tail-Variante, nicht die cat-Variante (um bestimmte Dateien nach> bestimmten neuen Strings permanent zu filtern, und bestimmte Aktionen> auslösen zu können).
Okay. Deine Script-Ausschnitt hat dann aber gar nichts mit dem
TO-Problem zu tun.
Gruß,
Frank
Frank M. schrieb:>> cat <file> | while read line>> Useless use of cat. Der Prozess ist hyperfluid.>> Ein>> while read line .... done <file>> macht dasselbe.
Und noch schlimmer.
Bei dem Konvolut
1
cat file | while read line; do ... done
kannst Du aufgrund der Pipe z.B. vergessen, in ... irgendwelche
Variablen zuzuweisen und deren Werte hinterher noch verwenden zu können.
Da guckt die Katze in die Röhre.
Mit
Frank M. schrieb:> Falsch: Ein echo erzeugt unter UNIX nur einen LF, aber kein CR. Der TO> bekommt das CRLF-Paar von der seriellen Schnittstelle und will das CR> nicht in der Protokoll-Datei, weil das eben UNIX-unüblich ist.
Wie sicher ist denn das?
Kilian schrieb:> Die Daten werden zwar aufgezeichnet, allerdings immer mit dem <CR><LF>> am Ende, was zur Folge hat, das in der Datei nach jedem String eine> Leerzeile entsteht.
Daraus erlese ich erstmal, dass eine Leerzeile zuviel da ist. Ob das
wirklich \r\n ist oder doch nur \n sehe ich da nicht, das vermutet
Kilian bis jetzt nur.
Ich vermute eher, das Kilian gar nicht weiß, dass es unter Linux nur das
\n ist.
Ich würde erstmal
1
echo-n$LINE
probieren, damit fügt echo kein zusätzlichen Zeilenmubruch an.
ein anderer schrieb:> Frank M. schrieb:>> Falsch: Ein echo erzeugt unter UNIX nur einen LF, aber kein CR. Der TO>> bekommt das CRLF-Paar von der seriellen Schnittstelle und will das CR>> nicht in der Protokoll-Datei, weil das eben UNIX-unüblich ist.> Wie sicher ist denn das?
Todsicher:
# echo "" | hexdump -c
0000000 \n
0000001
> Ich würde erstmal> echo -n $LINE> probieren, damit fügt echo kein zusätzlichen Zeilenmubruch an.
Der Befehl
read $LINE
liest alles bis zum Newline \n - aber ausschließlich.
Es kann also kein Newline in $LINE sein. Wenn Du echo mit -n aufruft,
dann hast Du eine ganz laaaaaaaaaange Zeile - höchstens durchsetzt mit
CR.
Dir fehlen essentielle UNIX-/Linux-Grundlagen.
> kannst Du aufgrund der Pipe z.B. vergessen, in ... irgendwelche> Variablen zuzuweisen und deren Werte hinterher noch verwenden zu können.
Korrekt. Denn hier wird die while-Schleife zu einem Kindprozess und die
Variablen sind dann nur lokal in der while-Schleife gültig. Ganz böse.
@Frank M. (ukw) Benutzerseite
>Der Befehl> read $LINE>liest alles bis zum Newline \n - aber ausschließlich.>Es kann also kein Newline in $LINE sein. Wenn Du echo mit -n aufruft,>dann hast Du eine ganz laaaaaaaaaange Zeile - höchstens durchsetzt mit>CR.
Wie paßt das zusammen mit dem Problem des TO?
Er hat schließlich offensichtlich zwei LF drin, weil er ja immer eine
zusätzliche Leerzeile drin hat.
Wenn read $LINE grundsätzlich keine LF enthält, sollte ja nur ein LF vom
Echo in der Datei landen. Wo kommt also das zweite her? Das CR selbst
hat wohl keine steuernde Wirkung unter unix, wenn ich es richtig sehe
(das wird wohl einfach als ^M angezeigt, und nicht als neue Zeile).
Jens G. schrieb:> Wenn read $LINE grundsätzlich keine LF enthält, sollte ja nur ein LF vom> Echo in der Datei landen. Wo kommt also das zweite her? Das CR selbst> hat wohl keine steuernde Wirkung unter unix, wenn ich es richtig sehe> (das wird wohl einfach als ^M angezeigt, und nicht als neue Zeile).
Das kommt darauf an. less bzw. vi zeigen es als ^M, bei cat sieht man
das CR gar nicht.
Grund: Der Consolentreiber wandelt das einzelne \n für das Terminal(!)
sowieso um in ein \r\n, damit der Cursor nicht nur eine Zeile runter
geht (NL), sondern auch noch an den Anfang der Zeile geht (CR), siehe
auch [1]. Ein weiteres CR vom cat ausgegeben bewirkt dann in dem Moment
gar nichts.
Aber wer weiß, mit welchem Programm der TO seine datei.txt betrachtet.
Da kann alles mögliche bei einem CR passieren.... Am besten macht er mal
einen Hexdump.
Meine vorgeschlagene Lösung
tr -d '\r' < /dev/ttyS0 | tee datei.txt
ist auf jeden Fall die sinnvollste Methode. Dann noch einen Hexdump
drauf und man kann auch sehen, was da tatsächlich über die Leitung geht
bzw. in der Datei landet. Alles andere ist Spekulation.
[1] "stty onlcr" für die Console steuert das: Wenn ein \n ausgegeben
werden soll, dann wird es auf dem TTY bei der Ausgabe in ein \r\n
gewandelt. Aber mit der Datei datei.txt hat dies nichts zu tun. Weitere
Infos dazu: man stty.
Außerdem könnte /dev/ttyS0 (jetzt also nicht die Console, sondern die
serielle Schnittstelle) natürlich noch mit dem Flag icrnl eingestellt
sein. In diesem Fall wird jedes reinkommende \r in ein \n gewandelt.
Folge: der TO sieht Doppel-Newlines! Ja, das passt.
Das kann er mit
stty -a </dev/ttyS0
schnell überprüfen.
In diesem Fall sollte er das mit
stty -icrnl </dev/ttyS0
abstellen.
Also Fazit:
Mit
stty -icrnl </dev/ttyS0
und anschließendem
tr -d '\r' < /dev/ttyS0 | tee datei.txt
sollte es dann sauber laufen.
Hallo zusammen!
Danke schonmal für die zahlreichen und auch hilfreichen Antworten.
Leider komme ich bei meinem Problem immer noch nicht so ganz weiter.
Hier nochmal die Anforderungen, die realisiert werden sollten:
-es kommt an ttyS0 ein String "bla bla bla<CR><LF>" an
-dieser sollte unverfälscht in eine Datei geschrieben werden (also mit
<CR><LF>)
-gleichzeitig sollte der String am Terminal ausgegeben werden,
allerdings immer ein String pro Zeile
Ich habe es bis jetzt so versucht:
1
stty -F /dev/ttyS0 9600 raw
2
3
read LINE < /dev/ttyS0
4
echo $LINE >> store.txt
5
echo $LINE
Allerdings ist das <CR> und <LF> in der Datei dann weg (wenn ich diese
auf einem Windows System betrachte).
Ich hab auch schon verschiedene Vorschlage aus diesem Thread versucht,
allerdings ohne Erfolg. Ein tee kann ich nicht verwenden, da bei jedem
Schleifendurchlauf die Dateigröße mit
überprüft werden muss.
Ich denke das es mit stty etwas zu tun hat. Aber ich steige da nicht
wirklich durch welche Parameter gesetzt werden müssen.
Kilian
Kilian schrieb:> -es kommt an ttyS0 ein String "bla bla bla<CR><LF>" an> -dieser sollte unverfälscht in eine Datei geschrieben werden (also mit> <CR><LF>)> -gleichzeitig sollte der String am Terminal ausgegeben werden,> allerdings immer ein String pro Zeile
Das klingt für mich immer noch nach tee:
1
tee < /dev/ttyS0 store.txt | tr -d \\r
Da ich grad nichts serielles hier habe, ist das ne reine Trockenübung.
> Ein tee kann ich nicht verwenden, da bei jedem> Schleifendurchlauf die Dateigröße mit
[...]
> überprüft werden muss.
Okay, DEN Teil habe ich auch erst jetzt gesehen. Dann eben ohne tee. Es
sei denn, es geht nur darum, die Datei ab einer bestimmten Größe zu
splitten? Dann könnte Dir split(1) helfen:
Ausgabe auf Dateien mit max. 10000 Zeilen aufteilen:
Kilian schrieb:> -dieser sollte unverfälscht in eine Datei geschrieben werden (also mit> <CR><LF>)
Also cp, cat oder dd.
> -gleichzeitig sollte der String am Terminal ausgegeben werden,
tee.
> allerdings immer ein String pro Zeile
tee </dev/ttyS0 datei.txt
> read LINE < /dev/ttyS0
Liest bis zum '\n' - ausschließend.
> echo $LINE >> store.txt
Schreibt den gelesenen Inhalt plus '\n'.
Hier fehlen allerdings wieder die doppelten Anführungszeichen, die ich
hier schon erwähnt habe. Damit dürfte auch Dein CR verschwinden, da
dieses ein Whitespace ist und ohne Anführungszeichen im Nirvana
verschwindet.
Also:
echo "$LINE" >> store.txt
> Allerdings ist das <CR> und <LF> in der Datei dann weg (wenn ich diese> auf einem Windows System betrachte).
Schlecht, Begründung siehe oben.
> Ich hab auch schon verschiedene Vorschlage aus diesem Thread versucht,> allerdings ohne Erfolg. Ein tee kann ich nicht verwenden, da bei jedem> Schleifendurchlauf die Dateigröße mit>> [code]> FS=$(ls l datei.txt | tr -s " " | cut -d " " -f 5)> if [ $FS -gt 5000000 ]
Bis dahin war davon aber keine Rede! Wenn Du hier dauernd die Aufgaben
neu stellst, macht das keinen Spaß mehr :-(
Da kann ich Dir nur noch den Tipp geben, ein eigenes kleines C-Programm
zu schreiben, was alle Deine oben genannten Anforderungen erfüllt. Ist
wirklich nicht schwierig.
Dann kannst Du Deine Anforderungen auch beliebig neu formulieren und
wieder neu lösen, ohne hier die Leser zu verärgern. Für mich ist
jedenfalls jetzt hier Schluss.