2009-06-26 10 views
7

Ich schreibe einen Filter (in einer Pipe, der für einen Terminal-Ausgang bestimmt ist), der manchmal eine Zeile "überschreiben" muss, die gerade aufgetreten ist. Es funktioniert, indem stdin übergeben wird, um Zeichen für Zeichen auszusteigen, bis eine \n erreicht ist, und dann spezielles Verhalten aufrufen. Mein Problem betrifft die Rückkehr zum Anfang der Zeile.Zurücksetzen des Terminal-Cursors zum Zeilenanfang mit aktiviertem Wrapping

Das erste, woran ich dachte, war eine \r oder die ANSI-Sequenz \033[1G. Wenn die Linie jedoch lang genug war, um das Terminal umwickelt zu haben (und somit zum Scrollen geführt hat), wird der Cursor nur zur aktuellen physischen Linie zurückbewegt.

Meine zweite Idee war, die Länge der Zeile zu verfolgen (Anzahl der Zeichen seit dem letzten \n übergeben), und dann echo \b, dass viele Male. Das geht jedoch schief, wenn die Zeile Steuerzeichen oder Escape-Sequenzen (und möglicherweise Unicode?) Enthielt.

Wenn Sie nicht nach allen speziellen Sequenzen suchen und diese verwenden, um meine Zeichenanzahl anzupassen, gibt es eine einfache Möglichkeit, dies zu erreichen?

Antwort

0

können Sie abfragen Terminal Dimensionen mit einem einfachen ioctl:

#include <sys/types.h> 
#include <sys/ioctl.h> 

// ... 

struct winsize ws; 
ioctl(1, TIOCGWINSZ, &ws); 

// ws.ws_col, ws.ws_row should now contain terminal dimensions 

Auf diese Weise können Sie verhindern, über das Ende der Linie und einfach verwenden, um die \r Methode Druck etwas.

+0

Danke für den Vorschlag! Leider gibt es ein paar Gründe, warum ich nicht denke, dass das für mich funktioniert: * Ich müsste immer noch spezielle Fallfolgen verfolgen, um zu wissen, wie nahe ich der Kante bin. * Ich bin vielleicht nicht das letzte Ding die Pipeline Für Kontext; Mein Filter ist Pattern-Matching und "Re-Writing" passender Strings, die in farbigen Escape-Sequenzen umgeben sind. Ich kann 'fgets()' nicht verwenden und ganze Strings gleichzeitig bearbeiten, da einige meiner Eingaben eine "dynamische" Ausgabe (z. B. einen inkrementierenden Fortschrittsstatus) erzeugen, die '\ n' für eine Weile nicht ausgibt. –

0
 
$ cat >test.sh <<'EOF' 
> #!/bin/sh 
> tput sc 
> echo 'Here is a really long multi-line string: .............................................................................................' 
> tput rc 
> echo 'I went back and overwrote some stuff!!!!' 
> echo 
> EOF 
$ sh test.sh 
I went back and overwrote some stuff!!!! ....................................... 
...................................................... 

Blick für die save_cursor und restore_cursor String-Funktionen in der terminfo Datenbank.

+0

Mein Verständnis ist, dass rc und sc die * physische * Position des Cursors speichern (entspricht \ 033 [s und \ 033 [u)]. Wenn der gesamte Inhalt des Terminals aufgrund des Zeilenumbruchs rollt, befindet sich der Anfang des Strings nicht mehr an derselben physischen Position. (Zumindest ist dies das Verhalten, das ich in xterm beobachte.) –

+0

Ah, das scheint auch in meinem Terminal zu passieren. Aww ... – ephemient

4

Selbst wenn es eine "magische Sequenz" gäbe, die beim Schreiben in eine Konsole die letzte geschriebene Zeile zuverlässig löschen würde, würden Sie die Zeile und die Sequenz trotzdem auf der Ausgabe finden (obwohl auf einer Konsole versteckt). Denken Sie darüber nach, was passieren würde, wenn jemand die Ausgabe in eine Datei schreiben oder die Pipe an andere Filter weiterleiten würde? Würden sie mit solchen Eingaben umgehen? Und sag mir nicht, dass du die Möglichkeit ausschließt, woanders als direkt in eine Konsole zu schreiben. Früher oder später wird jemand die Ausgabe umleiten wollen - vielleicht sogar Sie!

Der richtige Weg, dies zu tun ist, jede Zeile im Speicher während der Verarbeitung zu puffern und dann zu entscheiden, ob sie ausgegeben wird oder nicht. Es gibt wirklich keinen Ausweg.

+0

In der Tat, das war meine ursprüngliche Lösung. Die einzige Stelle, die bricht, ist für Dinge wie einen Fortschrittsprozentsatz, der sich im Laufe der Zeit erhöht (sich mit \ b überschreibend), wo es für mehrere Sekunden oder Minuten kein \ n gibt, also fgets() und so weiter für einen beträchtlichen Zeitraum blockieren.Übrigens habe ich die Möglichkeit, dass mein Filter nicht der letzte in der Pipeline ist (abgesehen von dem, was ich in einem früheren Kommentar gesagt habe), nicht berücksichtigt, da es keinen anderen Grund gibt, ihn als ein Werkzeug zur Verschönerung der Konsolenausgabe zu verwenden! –

+0

Ein kurzer Kommentar zum letzten Punkt in der Pipe: Kannst du nicht sicherstellen, dass stdout zu einem Terminal geht, wie 'ls' tut? –

Verwandte Themen