Wie drucke ich Zeichenfolgen, die durch TAB in Bash getrennt sind?

9

Ich versuche, zwei Zeichenfolgen zu drucken, die durch eine TAB getrennt sind. Ich habe versucht:

echo -e 'foo\tbar'
printf '%s\t%s\n' foo bar

Beide drucken:

foo     bar

Wobei das Leerzeichen zwischen den beiden tatsächlich 5 Leerzeichen beträgt (gemäß Auswahl der Ausgabe mit der Maus in Putty).

Ich habe auch versucht, STRG + V zu verwenden und beim Eingeben des Befehls die Tabulatortaste zu drücken, mit dem gleichen Ergebnis.

Was ist der richtige Weg, um das Drucken von Registerkarten als Registerkarten zu erzwingen, damit ich die Ausgabe auswählen und mit Registerkarten an eine andere Stelle kopieren kann?

Und die zweite Frage: Warum erweitert Bash Tabulatoren in Leerzeichen?

Update : Anscheinend ist dies ein Problem von Putty: /superuser/656838/how-to-make-putty-display-tabs-within-a-file-instead-of-changing-them-to -spaces

Asu
quelle
Warum nicht einfach entkommen? printf '%s\\t%s\n' foo bar
Valentin Bajrami
@ Steeldriver Danke, das ist sehr ähnlich zu dem, was ich brauche, aber letztendlich gibt es keine Lösung ...
Asu
1
@ Valentin Das gibt aus foo\tbar...
wjandrea
1
Trotz der Tatsache, dass Sie bereits wissen, dass Sie ein Problem mit Ihrem Terminal haben: Bash selbst interpretiert $'\t'als Tabulator. So können Sie Zeichenfolgen wie diese immer verketten - zum Beispiel als Zuordnung: v='This is'$'\t''a test'Und das drucken Sie es buchstäblich, zBprintf '%s' "$v"
rexkogitans

Antworten:

9

Wie ilkkachu sagte, ist dies kein Problem mit bash, sondern mit dem Terminalemulator, der Tabulatoren in Leerzeichen bei der Ausgabe konvertiert.

Wenn Sie verschiedene Terminals, Putty, Xterm und Konsole überprüfen, werden Registerkarten in Leerzeichen konvertiert, während dies bei urxvt und gnome-terminal nicht der Fall ist. Eine andere Lösung besteht darin, die Terminals zu wechseln.

JoL
quelle
3
Dies kann auch vom tty-Treiber nach dem Ausführen durchgeführt werden stty tab3.
Stéphane Chazelas
14

Das Leerzeichen zwischen den beiden ist tatsächlich 5 Leerzeichen.

Nein, ist es nicht. Nicht in der Ausgabe von echooder printf.

$ echo -e 'foo\tbar' | od -c
0000000   f   o   o  \t   b   a   r  \n
0000010

Was ist der richtige Weg, um das Drucken von Registerkarten als Registerkarten zu erzwingen, damit ich die Ausgabe auswählen und mit Registerkarten an eine andere Stelle kopieren kann?

Dies ist ein anderes Problem. Es geht nicht um die Shell, sondern um den Terminalemulator, der die Tabulatoren bei der Ausgabe in Leerzeichen konvertiert. Viele, aber nicht alle machen das.

Es ist möglicherweise einfacher, die Ausgabe mit Tabulatoren in eine Datei umzuleiten und von dort zu kopieren oder in unexpandder Ausgabe Leerzeichen in Tabulatoren zu konvertieren. (Obwohl es auch nicht wissen kann, mit welchem ​​Leerzeichen Tabs anfänglich waren, wird es, wenn möglich, alles in Tabs konvertieren.) Dies hängt natürlich davon ab, was genau Sie mit der Ausgabe tun müssen.

ilkkachu
quelle
Ich meinte, wenn ich versuche, die Ausgabe auszuwählen, wird sie als 5 Leerzeichen behandelt. Vielen Dank für das 'od -c', um den Inhalt der Befehlsausgabe zu überprüfen.
Asu
1
@Asu Ich denke er versteht das. Seine Lösung besteht darin, die Ausgabe auf andere Weise zu erhalten, da nicht garantiert wird, dass der Terminalemulator Registerkarten als Registerkarten belässt, wenn Sie sie im Fenster auswählen. Ich habe es jedoch nur überprüft und während Putty, Xterm und Konsole Tabs in Leerzeichen konvertieren, tun dies urxvt und gnome-terminal nicht. Eine andere Lösung besteht darin, die Terminals zu wechseln.
JoL
@JoL Ja, das ist die Schlussfolgerung, zu der ich vor einer Minute gekommen bin, und ich denke, es wäre die akzeptierte Antwort, wenn jemand sie als solche veröffentlichen möchte ...
Asu
1
@Asu, ja, ich habe darüber nachgedacht, das Problem nur manuell zu umgehen. Es wäre ärgerlich, das tun zu müssen, aber ich gebe zu, ich hatte nicht bemerkt, dass es Terminalemulatoren gibt, die das Kopieren von Registerkarten unterstützen. Ein Wechsel zu einem, der dies tut, wäre natürlich eine viel bessere Lösung!
Ilkkachu
4

In printf '%s\t%s\n' foo bar, printfgibt aus foo<TAB>bar<LF>.

f, o, b, aUnd rsind einfach breite graphischen Zeichen.

Nach Erhalt dieser Zeichen zeigt das Terminal eine entsprechende Glyphe an und bewegt den Cursor eine Spalte nach rechts, es sei denn, er hat bereits den rechten Bildschirmrand erreicht (Papier in Original-Fernschreibmaschinen). In diesem Fall wird möglicherweise eine Zeile eingezogen und kehren Sie zum linken Bildschirmrand zurück (Wrap) oder verwerfen Sie das Zeichen je nach Terminal und Konfiguration.

<Tab>und <LF>sind zwei Steuerzeichen . <LF>(auch bekannt als newline) ist das Zeilentrennzeichen in Unix-Text, aber bei Terminals wird nur eine Zeile eingezogen (bewegen Sie den Cursor eine Position nach unten). Der Terminaltreiber im Kernel übersetzt ihn also tatsächlich in <CR>(Rückkehr zum linken Bildschirmrand) <LF>(Cursor nach unten) ( stty onlcrnormalerweise standardmäßig aktiviert).

<Tab> Weist das Terminal an, den Cursor zum nächsten Tabulator zu bewegen (der bei den meisten Terminals standardmäßig 8 Positionen voneinander entfernt ist, aber auch so konfiguriert werden kann, dass er überall eingestellt werden kann), ohne die Lücke mit Leerzeichen zu füllen.

Wenn diese Zeichen also alle 8 Spalten an ein Terminal mit Tabulatoren gesendet werden, während sich der Cursor am Anfang einer leeren Zeile befindet, führt dies zu:

foo     bar

in dieser Zeile auf dem Bildschirm gedruckt. Wenn sie gesendet werden, während sich der Cursor an dritter Stelle in einer Zeile befindet xxxxyyyyzzzz, die Folgendes enthält , führt dies zu:

xxfooyyybarz

Auf Terminals, die keine Tabellierung unterstützen, kann der Terminaltreiber so konfiguriert werden, dass diese Registerkarten in Leerzeichenfolgen übersetzt werden. ( stty tab3).

Das SPC-Zeichen in Original-Fernschreibmaschinen würde den Cursor nach rechts bewegen, während die Rücktaste ( \b) ihn nach links bewegen würde. In modernen Terminals bewegt sich SPC nach rechts und löscht auch (schreibt ein Leerzeichen, wie Sie es erwarten würden). Der Anhänger von \bmusste also etwas Neueres als ASCII sein. Auf den meisten modernen Terminals, es ist eigentlich eine Folge von Zeichen: <Esc>, [, C.

Es gibt mehr Escape-Sequenzen, um nZeichen nach links, rechts, oben, unten oder an einer beliebigen Position auf dem Bildschirm zu bewegen . Es gibt andere Escape-Sequenzen, um Teile von Linien oder Bereichen des Bildschirms usw. zu löschen (mit Leerzeichen zu füllen).

Diese Sequenzen werden in der Regel durch visuelle Anwendungen wie vi, lynx, mutt, dialogwo Text an beliebigen Positionen auf dem Bildschirm geschrieben wird.

Mit allen X11-Terminalemulatoren und einigen anderen Nicht-X11-Emulatoren wie GNU können screenSie jetzt Bereiche des Bildschirms zum Kopieren und Einfügen auswählen. Wenn Sie einen Teil dessen auswählen, was Sie im viEditor sehen, möchten Sie nicht alle Escape-Sequenzen kopieren, die zur Erstellung dieser Ausgabe verwendet wurden. Sie möchten den dort angezeigten Text auswählen.

Zum Beispiel, wenn Sie ausführen:

printf 'abC\rAC\bB\t\e[C\b\bD\n'

Welche einen Editor Sitzung simuliert , wo Sie eingeben abC, gehen Sie an den Anfang zurück, ersetzen abmit AC, Cmit B, Umzug in den nächsten Tabstopp, dann eine weitere Spalte nach rechts, dann zwei Spalten nach links, dann eingeben D.

Sie sehen:

ABC    D

Das heißt, ABCeine Lücke von 4 Spalten und D.

Wenn Sie dies mit der Maus in xtermoder auswählen, puttywerden in der Auswahl ABC4 Leerzeichen und Dnicht gespeichert abC<CR>AC<BS>B<Tab><Esc>[C<BS><BS>D.

Was in der Auswahl endet, ist das, was printfsowohl vom Terminaltreiber als auch vom Terminalemulator gesendet, aber nachbearbeitet wurde.

Für andere Arten der Transformation siehe die <U+0065><U+0301>( egefolgt von einem kombinierten akuten Akzent) geändert in <U+00E9>( édie vorkomponierte Form) durch xterm.

Oder echo abcdas wird ABCvom Terminaltreiber übersetzt, bevor es nach a an das Terminal gesendet wird stty olcuc.

Nun, <Tab>wie <LF>ist eines dieser wenigen Steuerzeichen, die tatsächlich manchmal in Textdateien gefunden werden (auch <CR>in MSDOS-Textdateien und manchmal <FF>für Seitenumbrüche).

Einige Terminalemulatoren kopieren sie daher nach Möglichkeit in die Puffer zum Kopieren und Einfügen, um sie zu erhalten (dies ist jedoch im Allgemeinen weder der Fall <CR>noch der Fall <LF>).

In VTE-basierten Terminals wie gnome-terminalkönnen Sie beispielsweise feststellen, dass bei Auswahl des Ausgangs printf 'a\tb\n'in einer leeren Zeile gnome-terminaltatsächlich 7 Leerzeichen und a\tbin der X11-Auswahl agespeichert werden b.

Aber für die Ausgabe printf 'a\t\bb\n', es speichert a, 6 Räume und b, und für printf 'a\r\tb\n', a, 7 Räume und b.

Es gibt andere Fälle, in denen die Terminals versuchen, die tatsächliche Eingabe zu kopieren, z. B. wenn Sie nach dem Ausführen zwei Zeilen auswählen, in printf 'a \nb\n'denen dieser unsichtbare nachfolgende Speicherplatz erhalten bleibt. Oder wenn Sie zwei Zeilen auswählen, wird kein LF-Zeichen eingefügt, wenn die beiden Zeilen durch Umbrechen am rechten Rand entstehen.

Wenn Sie nun die Ausgabe von printfin der CLIPBOARD- X11Auswahl speichern möchten, tun Sie dies am besten direkt wie folgt mit:

printf 'foo\tbar\n' | xclip -sel c

Beachten Sie, dass , wenn Sie fügen , dass in den xtermmeisten anderen Terminals oder xtermersetzt eigentlich , dass \nmit , \rdenn das ist das Zeichen xtermsendet , wenn Sie drücken Enter(und der Terminal - Treiber kann es übersetzen zurück zu \n).

Stéphane Chazelas
quelle
Das ist sehr aufschlussreich, danke. Ich habe die xclip-Lösung ausprobiert und sie funktioniert. Aber es macht nicht genau das, was ich mir vorgestellt habe und erfordert X11. Vielleicht wird dies irgendwann nützlich sein, danke!
Asu
@Asu X11übernimmt die Auswahl zum Kopieren und Einfügen in Terminalemulatoren wie xtermoder puttyunter Unix. Andere Terminalemulatoren verfügen möglicherweise über einen eigenen Mechanismus zum Kopieren und Einfügen sowie über Möglichkeiten zum Speichern beliebiger Inhalte, z. B. die Befehle readbuf und registerim GNU-Bildschirm.
Stéphane Chazelas