Wie liest man über 4k Input ohne neue Zeilen auf einem Terminal?

25

Ich habe also viele Daten OHNE NEUE ZEILEN in der Zwischenablage (es ist eine große SVG-Datei in einer Zeile). ich ging

$ cat >file.svg

Dann wurde versucht einzufügen (in Gnome Terminal), aber nur die ersten 4kB Zeichen wurden akzeptiert.

Ich gehe davon aus, dass dies eine Readline-Funktion / Einschränkung ist.

Gibt es eine Möglichkeit, aus STDIN zu lesen, um dieses Problem zu vermeiden?

BEARBEITEN

Testfall: Erstellen Sie eine Demo-Datei. Dieser hat ~ 4k "=" Symbole, gefolgt von "foo bar".

{ printf '=%.0s' {1..4095} ; echo "foo bar" ; } > test.in

Kopieren Sie das in Ihre Zwischenablage

xclip test.in

(Wenn Sie zum Einfügen mit der mittleren Maustaste klicken möchten) oder

xclip -selection clipboard test.in

(wenn Sie Strg-Umschalt-Einfügen verwenden möchten, um es einzufügen)

Dann cat >test.outeinfügen (welcher Art auch immer). Drücken Sie Strg-D, um den Stream zu beenden. cat test.out- siehst du "foo bar"?

In meinem Setup (Ubuntu 12.04, Gnome Terminal, zsh) sehe ich beim Einfügen nur das =und nicht das foo bar. Das gleiche gilt, wenn ich mich umsehe test.out.

Kunstvollroboter
quelle
Sind Sie sicher, dass Ihre SVG-Datei vollständig in Ihre Zwischenablage eingelesen wurde?
Lgeorget
Was ist Ihr aktuelles Problem? Wie speichere ich den Inhalt der Zwischenablage in einer Datei? Wenn ja, gibt es andere Möglichkeiten als das Einfügen in das Terminal.
Lgeorget
wie viel ist N in deinem Fall? Ich habe es mit 2kB XML-Daten (inkl. LF) kein Problem versucht.
fduff
1
@artfulrobot Ein Vordergrundprozess interagiert direkt mit dem tty / pty. Die Shell ist nicht beteiligt. Sie können dies daran erkennen, dass Sie keine Readline-Funktionen (Befehle zum Bearbeiten / Springen, Verlauf usw.) in Programmen haben, wenn diese Readline oder eine andere Eingabebibliothek selbst nicht verwenden.
Jofel
1
Dies ist keine Readline-Einschränkung - Readline und Bash sind hier nicht beteiligt. Dies ist eine Einschränkung der Terminalschnittstelle.
Gilles 'SO- hör auf böse zu sein'

Antworten:

22

Wenn ich die Quelle richtig verstehe, wird unter Linux die maximale Anzahl von Zeichen, die auf einem Terminal auf einmal gelesen werden können, durch N_TTY_BUF_SIZEdie Kernelquelle bestimmt. Der Wert ist 4096.

Dies ist eine Einschränkung der Terminalschnittstelle, insbesondere des kanonischen ("gekochten") Modus, der einen äußerst groben Zeileneditor bietet (Rücktaste, Eingabe, Ctrl+ Dam Anfang einer Zeile für das Dateiende). Es geschieht völlig außerhalb des Prozesses, der liest.

Sie können das Terminal in den Raw-Modus schalten, wodurch die Leitungsverarbeitung deaktiviert wird. Es deaktiviert auch Ctrl+ Dund andere Feinheiten, was Ihr Programm zusätzlich belastet.

Dies ist eine alte Unix-Einschränkung, die nie behoben wurde, weil es wenig Motivation gibt. Menschen treten nicht in so lange Schlangen ein. Wenn Sie Eingaben von einem Programm einspeisen, leiten Sie die Eingaben Ihres Programms von einer Datei oder einer Pipe um.

Um beispielsweise den Inhalt der X-Zwischenablage zu verwenden, leiten Sie von xseloder xclip. In Ihrem Fall:

xsel -b >file.svg
xclip -selection clipboard >file.svg

Entfernen Sie -boder -selection clipboard, um die Auswahl X (die durch Markieren mit der Maus festgelegt wird) anstelle der Zwischenablage zu verwenden.

Verwenden Sie unter OSX, um pbpasteden Inhalt der Zwischenablage einzufügen (und pbcopyfestzulegen).

Sie können über SSH auf die X-Zwischenablage zugreifen, wenn Sie die X11-Weiterleitung aktivieren ssh -X(was einige Server möglicherweise verbieten). Wenn Sie nur verwenden können , sshohne X11 - Weiterleitung, können Sie verwenden scp, sftpoder sshfseine Datei zu kopieren.

Wenn das Einfügen die einzige Lösung ist, weil Sie die Zwischenablage nicht weiterleiten können oder nicht einfügen, sondern beispielsweise die Eingabe in eine virtuelle Maschine vortäuschen, können Sie die Daten auch in Zeilenumbrüche codieren. Base64 ist dafür gut geeignet: Es wandelt beliebige Daten in druckbare Zeichen um und ignoriert Leerzeichen beim Dekodieren. Dieser Ansatz hat den zusätzlichen Vorteil, dass er beliebige Daten in der Eingabe unterstützt, sogar Steuerzeichen, die das Terminal beim Einfügen interpretieren würde. In Ihrem Fall können Sie den Inhalt kodieren:

xsel -b | base64 | xsel -b

dann dekodiere es:

base64 -d
 Paste
Ctrl+D
Gilles 'SO - hör auf böse zu sein'
quelle
Beachten Sie, dass es bei der Verwendung von mehr als xsel4 KByte
Patrick,
14

Die Grenzen Sie in läufst ist die maximale Größe einer Zeile im kanonischen Eingabemodus , MAX_CANON.

Im kanonischen Eingabemodus stellt der tty-Treiber grundlegende Zeilenbearbeitungsdienste bereit, sodass das Userspace-Programm dies nicht muss. Es hat nicht annähernd so viele Funktionen wie readline, erkennt jedoch einige konfigurierbare Sonderzeichen wie Erase (normalerweise Backspace oder Delete) und Kill (normalerweise Ctrl-U).

Am wichtigsten für Ihre Frage ist, dass der kanonische Modus die Eingabe puffert, bis das Zeilenendezeichen angezeigt wird. Da sich der Puffer im tty-Treiber im Kernel-Speicher befindet, ist er nicht sehr groß.

Sie können den kanonischen Modus mit stty cbreakoder ausschalten stty -icanonund dann einfügen. Dies hat den entscheidenden Nachteil, dass Sie mit Strg-D kein EOF senden können. Das ist eine andere Sache, für die der kanonische Modus verantwortlich ist. Sie können das catmit Strg-C beenden, da die signalerzeugenden Zeichen durch ein separates Flag ( stty rawoder stty -isig) gesteuert werden .

Das Rätsel für mich ist, warum Sie, da Sie bereits bewiesen haben, dass Sie davon wissen xclip, nicht nur das verwenden, xclip -o > filesondern auch dascat


quelle
1
Das Rätsel lässt sich leicht lösen: Es scheint, dass artfulrobot eine Datei auf einem Remote-Host schnell mit Daten aus der Zwischenablage füllen möchte. In der Remote-Shell gibt es normalerweise keinen direkten Zugriff auf die lokale Zwischenablage über xclip.
Jofel
3
Ah, gute alte Upload-by-Paste. Wenn ich einen dieser Schritte ausführen müsste und es kein einfacher Text wäre, würde ich ihn uncodieren, anstatt zu versuchen, den tty-Treiber zu überreden, ihn durchzulassen. Auch einfacher Text mit enormen Linien könnte auf diese Weise behandelt werden.
2

Wenn Sie tun:

stty eol =

Und führen Sie dann die in Ihrem EDIT vorgeschlagene Demo aus. Sie sehen die foo-Leiste im Ausdruck von test.out . Die Zeilendisziplin des Terminals spült seine Ausgabe an den Leser, während es jedes spezielle EOL-Zeichen in Ihrer Eingabe liest .

Ein Linux-Terminal im kanonischen Modus - wie es mit stty icanonoder wahrscheinlich nur mit konfiguriert werden kann stty sane- verarbeitet die folgenden speziellen Eingabezeichen ...

  • eof
    • Standard: ^D
    • Beendet eine Eingabezeile und spült die Ausgabe zum Lesegerät. Da es aus der Eingabe entfernt wird und als einziges Zeichen in einer Zeile eingegeben wird, wird es als Null- Lese- oder Dateiende an den Reader übergeben.
  • eol
    • Standard: nicht zugewiesen
    • Beendet auch eine Eingabezeile, wird jedoch nicht aus der Eingabe entfernt.
  • töten
    • Standard: ^U
    • Löscht alle gepufferten Eingaben.
  • löschen
    • Standard: ^H (oder möglicherweise @oder ^?auf einigen Systemen)
    • Löscht das zuletzt gepufferte Eingabezeichen.

Wenn iexten auch gesetzt ist - wie stty icanon iextenoder wahrscheinlich auch nur - stty sane, wird ein kanonisches Linux-Terminal auch ...

  • eol2
    • Standard: nicht zugewiesen
    • Beendet auch eine Eingabezeile und wird auch nicht aus der Eingabe entfernt.
  • werase
    • Standard: ^W
    • Löscht das letzte gepufferte Eingangswort .
  • rprnt
    • Standard: ^R
    • Druckt alle gepufferten Eingaben erneut.
  • Nächste
    • Standard: ^V
    • Entfernt jegliche besondere Bedeutung in Bezug auf die Zeilendisziplin für das unmittelbar folgende Eingabezeichen.

Diese Zeichen werden behandelt, indem sie aus dem Eingabestream entfernt werden - mit Ausnahme von eol und eol2 - und die zugehörige Spezialfunktion ausgeführt werden, bevor der verarbeitete Stream an den Reader übergeben wird. Dies ist normalerweise Ihre Shell, kann aber auch die Vordergrundprozessgruppe sein .

Andere spezielle Eingabezeichen, die ähnlich gehandhabt werden, aber unabhängig von einer beliebigen Einstellung von icanon konfiguriert werden können , umfassen das isig set - set wie stty isigund wahrscheinlich auch in einer vernünftigen Konfiguration enthalten:

  • Verlassen
    • Standard: ^\
    • Leert alle gepufferten Eingaben (falls noflsh nicht gesetzt ist) und sendet SIGQUIT an die Vordergrund-Prozessgruppe - was wahrscheinlich einen Core-Dump erzeugt.
  • verdächtig
    • Standard: ^Z
    • Leert alle gepufferten Eingaben (falls noflsh nicht gesetzt ist) und sendet SIGTSTP an die Vordergrund-Prozessgruppe. Die angehaltene Prozessgruppe kann wahrscheinlich mit einer kill -CONT "$!"oder nur fgmit einer ( set -m) jobgesteuerten Shell fortgesetzt werden .
  • intr
    • Standard: ^C
    • Leert alle gepufferten Eingaben (falls noflsh nicht gesetzt ist) und sendet SIGINT an die Vordergrund-Prozessgruppe.

Und das ixon- Set - konfiguriert wie stty ixonund normalerweise auch in einer vernünftigen Konfiguration enthalten:

  • halt
    • Standard: ^S
    • Stoppt die gesamte Ausgabe an den Reader, bis entweder Start in Eingabe gelesen wird oder - wenn ixany ebenfalls gesetzt ist - mindestens ein weiteres Zeichen gelesen wird.
  • Start
    • Standard: ^Q
    • Startet die Ausgabe neu, wenn sie zuvor mit stop gestoppt wurde .
  • Sowohl stop als auch start werden bei der Verarbeitung aus der Eingabe entfernt. Wenn jedoch die Ausgabe aufgrund eines Zeichens in der Eingabe neu gestartet wird, wenn ixany festgelegt ist, wird dieses Zeichen nicht entfernt.

Sonderzeichen, die auf anderen Nicht-Linux-Systemen behandelt werden, können sein:

  • spülen
    • Standard: ^O
    • Schaltet das Löschen und Löschen von gepufferten Eingaben ein und wird aus der Eingabe entfernt.
  • dsusp
    • Standard: nicht zugewiesen
    • Leert alle gepufferten Eingaben nur, wenn der Leser das zugewiesene spezielle Eingabezeichen liest und dann SIGTSTP sendet.

Und möglicherweise...

  • swtch
    • Standard ^@ (Bedeutung \0oder NUL)
    • Schaltet die Vordergrund-Shell-Ebenen um. Zur Verwendung mit der shl Shell-Layer- Anwendung auf einigen Systemen.
    • Eine Implementierung, shldie ptys multiplext und daher mit der Auftragssteuerung kompatibel ist, und nicht das swtch- abhängige Verhalten der ursprünglichen Implementierung, kann in der heirloom-toolchestTool-Suite frei gewählt werden.

Für ein klareres Bild, wie und warum (und vielleicht warum nicht) diese Eingabefunktionen gehandhabt werden, konsultieren Sie man 3 termios.

Alle oben genannten Funktionen können - falls zutreffend - wie zugewiesen (oder neu zugewiesen) werdensttyfunction assigned-key . Um eine einzelne Funktion zu deaktivieren, gehen Sie wie folgt vor . Alternativ können Sie, wie verschiedene Versuche mit Zuweisungen für eine der oben genannten Zeilenbearbeitungsfunktionen mit allen GNU-, AST- oder Erbstück- Implementierungen zu signalisieren scheinen, als NUL- Zuweisung für eine Funktion angeben, dass sie unter meinem Linux nicht zugewiesen ist System.sttyfunction^-sttysttyfunction^@

Wahrscheinlich sehen Sie ein Echo dieser Zeichen, wenn Sie sie eingeben (wie wahrscheinlich mit [-] ctlecho konfiguriert werden kann ) , aber dies ist nur eine Markierung, die Ihnen zeigt, wo Sie es getan haben - das Programm, das Ihre Eingabe empfängt, hat keine Ahnung, dass Sie es tun Sie haben sie getippt (mit Ausnahme von eol [2] ) und erhalten nur eine Kopie Ihrer Eingabe, auf die die Zeilendisziplin ihre Effekte angewendet hat.

Die Handhabung der verschiedenen Zeilenbearbeitungsfunktionen durch das Terminal hat zur Folge, dass es die Eingabe in gewissem Maße puffern muss, um auf die Funktionen zu reagieren, die Sie ihm anzeigen, dass dies der Fall sein sollte - und daher kann es keine unbegrenzte Zufuhr von Eingaben geben, die Sie könnten jederzeit töten . Der Zeilenpuffer ist genauer gesagt der Kill - Puffer.

Wenn legen Sie die EOL oder EOL2 Zeichen bis zu einem gewissen Trennzeichen , die in der Eingabe auftritt - auch wenn weder ein Newline oder ein Return - Zeichen, zum Beispiel - dann werden Sie nur in der Lage sein , zu töten bis zu dem Punkt , dass es zuletzt aufgetreten und Ihren Kill - Puffer wird so weit wie möglich verlängert, bis die nächste Zeile ( oder eine neue Zeile (oder ein Return, wenn icrnl gesetzt ist und igncr nicht)) in der Eingabe auftritt.

mikeserv
quelle
1

catakzeptiert eine beliebige Anzahl von Zeichen, wie Sie zum Beispiel sehen könnten cat /dev/random > test.bin(tun Sie es nicht, es sei denn, Sie wissen, wie Sie es stoppen können :). Ich habe versucht, eine große Datei zu kopieren und einzufügen cat > test.txt. Alle Zeilen sind in der Datei gelandet, unabhängig davon, ob ich mit Ctrl- coder Ctrl- abgebrochen habe. Im ersten dFall wurden jedoch nicht alle Zeilen auf dem Terminal gedruckt . Ich glaube, das liegt daran, dass catder Druckvorgang gepuffert wird und vor jedem Druckvorgang auf einen vollständigen Textpuffer oder eine direkte Eingabe vom Terminal gewartet wird.

Auf meinem System glaube ich, dass die Puffergröße 4096 (2 ^ 12) Bytes beträgt: Erstellen Sie eine Datei mit 4095 Bytes (printf '1234567890%.0s' {1..409} && printf 12345) > test.in, laden Sie diese mit in den Kopierpuffer xclip test.in, starten Sie sie cat > test.out, fügen Sie sie mit Shift- ein Insertund beenden Sie den Stream mit Ctrl- d. Fügen Sie nun ein Byte mit hinzu printf '6' >> test.in, und der Stream wird zweimal gedruckt : Einmal in der catAusgabe (alle 4096 Bytes) und die letzten 4095 Bytes erneut in der Shell nach dem Beenden.

l0b0
quelle
+1 In meinem Fall hing es auch von der verwendeten Zwischenablage ab. Wenn ich den Auswahlpuffer (Einfügen mit mittlerem Klick) verwendet habe, habe ich nur die ersten 4542 Zeilen meiner Testdaten gesehen (aber alle sind in der erstellten Datei gelandet), aber ich habe die X-Zwischenablage (Strg + C / Strg + V) verwendet alles davon. In beiden Fällen wurden alle Daten in die resultierende Datei gedruckt, in den ersteren Fällen wurden nur Teildaten im Terminal angezeigt.
terdon
1
Ich verstehe nicht das gleiche Verhalten. Siehe bearbeitete Frage
artfulrobot
0

Eine Lösung besteht darin, es in einen Editor einzufügen, der lange Zeilen unterstützt, z. B. vim.

Wenn Sie vim verwenden, rufen Sie zuerst den :pasteEinfügemodus mit auf, bevor Sie den Einfügemodus mit iaufrufen und den Text einfügen .

Hjulle
quelle