Von Zeit zu Zeit passiert es, dass ich eine Binärdatei entweder von curl oder vom lokalen Dateisystem cate. In den meisten Fällen kann das defekte Terminal repariert werden mitzurücksetzen. In anderen Fällen, insbesondere wenn die Binärdatei groß ist, bleibt das Terminal mehrere Minuten hängen und gibt eine Ausgabe wie diese aus:
auch bekannt
c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;
2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;
2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;
2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;2c1;
Ich habe drei Fragen zu diesem Szenario:
- Was bedeutet 2c1 und warum druckt das Terminal dies?
- Haben Sie schon einmal
cat
in der Praxis erlebt, wie man sich in einer interaktiven Sitzung vor diesem unerwünschten Verhalten schützt? - Haben Sie Vorschläge, wie man eine solche Katze programmiert (in Cee oder Golang)
Mein erster Instinkt war, „cat“ in eine Funktion zu packen, um dies zu erkennen, aber ich erkannte bald, dass es ziemlich schwierig ist, das richtig hinzubekommen, und dass es zahlreiche Randfälle geben würde.
function cat() {
# warn user if
# - argument 1 is a large executable
# - argument 1 to the previous command in the a pipe-chain looks like a large binary
# abort if
# - session is interactive and we are able to detect 2c1 garbage
}
Eine praktische Lösung könnte darin bestehen, bei „unsicheren“ Eingaben immer less (mit LESSPIPE) zu verwenden, aber diese Frage betrifft nicht Pager. Ich kenne less und lesspipe. Ich verwende sie jeden Tag aktiv. Vielleicht less+lesspipeist die Lösung für dieses Problem, die der Autor (die Autoren von less) vor etwa 20-30 Jahren mit dem gleichen Problem umgesetzt hat.
Allerdings unterscheidet sich Cat in mehr als einer Hinsicht von einem „Pager“ … Cat ist in erster Linie nicht interaktiv. Das ist für mich von Bedeutung.
Der Vorschlag zu less+lesspipe ist (meiner Meinung nach) in praktischer Hinsicht wirklich gut, aber ich mache mir mehr Sorgen um die Details der Steuerzeichen, speziellen Escape-Sequenzen und wie verschiedene Terminals diese Eingaben verarbeiten.
Mich interessieren eher die technischen Details der Steuerzeichen und wie Terminals oder Shells „Müll“ und Steuerzeichen interpretieren. Ich frage nicht „Wie würden Sie dieses Problem lösen?“ Ich frage „Warum behandelt das Terminal Binärdateien auf diese Weise?“
Antwort1
Ich würde stattdessen verwenden less
, das vor Binärdateien warnt und auf einigen Systemen verschiedene Arten verarbeiten kann (z.Bunter CentOS 7 kann ich das tun less file.rpm
und die Dateien im RPM sehen). Ich glaube, das heißt „lesspipe“.
Außerdem können Sie beim nächsten Mal, wenn das passiert, versuchen, reset
oder tput reset
zum Normalzustand zurückzukehren. Diese senden Escape-Sequenzen an das Terminal, die es anweisen, auf einen vernünftigen Standardzustand zurückzusetzen, und führen auch das Äquivalent aus, stty sane
das die Einstellungen der TTY-Gerätedatei auf einen vernünftigen Standardwert ändert (das Dumpen einer Binärdatei sollte diese jedoch nicht beeinflussen). reset
kann auch die Vorstellung der TTY-Gerätedatei von der Fenstergröße des Terminals oder Terminalemulators (wie von gemeldet tty size
) bei den Terminals korrigieren, die die Abfrage unterstützen.
Antwort2
Niemand hat „Strings“ erwähnt. Obwohl Strings nicht genau wie cat ist, druckt es nur die Textstrings aus fortlaufenden Textdaten, um die Anzeige im Terminal sicherer zu machen. Normalerweise ist es im Binutils-Paket enthalten. Es ist ein praktisches Programm, um schnell sicherzustellen, dass Sie keine binäre Ausgabe von gedruckten Daten erhalten, und es ist auch nützlich, wenn Sie nur die fortlaufenden nicht-binären Daten sehen möchten. Beachten Sie, dass standardmäßig nur fortlaufende Textabschnitte mit 4 oder mehr ASCII-Zeichen ausgedruckt werden. Dies kann mit der Option -n angepasst werden.
Antwort3
Sie interagieren mit einem Terminal oder Terminalemulator über eine serielle Leitung oder ein Pseudo-TTY-Gerät (das eine serielle Leitung emuliert).
Obwohl es im Kernel ein Softwaremodul gibt, das in der Mitte als eine Art Anpassungsschicht sitzt und einige Transformationen durchführt (wird später kurz erläutert), gehen Sie normalerweise wie folgt vor:
- Senden Sie über diese serielle Leitung einen Bytestrom an das Terminal, den das Terminal entweder als Glyphen interpretiert, die auf seinem Bildschirm angezeigt werden sollen, oder als spezielle Anweisungen, um sein Verhalten zu ändern.
- Im Gegenzug sendet das Terminal über ein anderes Kabel auf dieser seriellen Leitung einen Bytestrom an den Computer, um dem Computer mitzuteilen, was Sie eingegeben haben, oder um auf einige der empfangenen Steuerabfragen zu antworten.
Ein Terminal könnte beispielsweise als ISO8859-1-Terminal (auch als Latin1-Terminal bezeichnet) konfiguriert werden. Das bedeutet, dass es beim Empfang der Zeichenfolge 0x53 0x74 0xe9 0x70 0x68 0x61 0x6e 0x65 diese so interpretiert, als würden die Glyphen S
, t
, é
, p
, h
, a
, n
, e
an der aktuellen Cursorposition auf seinem Bildschirm dargestellt. Und umgekehrt Ssendet das Terminal das Byte 0x53, wenn der Benutzer eingibt.
Bytewerte im Bereich 0 bis 0x1f werden interpretiert alsKontrolleZeichen. Das heißt, sie werden nicht als Glyphen dargestellt, sondern haben eine besondere Bedeutung.
Zum Beispiel:
- 0x7 (BEL) generiert einen Audio- oder Videoalarm
- 0x8 (BS) bewegt den Cursor nach links
- 0xa (LF) bewegt den Cursor nach unten
- 0xd (CR) bewegt den Cursor zur ersten Spalte des Bildschirms
- 0x9 (TAB) bewegt den Cursor zur nächsten Tabellierung
Es gibt nur 32 Steuerzeichen in diesem Bereich und die meisten Terminals haben viele weitere Funktionen oder Möglichkeiten, diese zu steuern. Darüber hinaus können Sie Sequenzen von mehr alseinsByte zur Steuerung Ihres Terminals. Bei den meisten Terminals und den meisten dieser Sequenzen ist das erste Byte 0x1b (ESC), gefolgt von einem oder mehreren Bytes.
So gibt es beispielsweise zwar Steuerzeichen, um den Cursor wie oben gezeigt nach links oder unten zu bewegen, jedoch keine, um ihn nach rechts oder oben zu bewegen (bei Fernschreibmaschinen wurde ursprünglich die Bewegung nach rechts mit der Leertaste erledigt, was bei CRT-Terminals jedoch löscht, was sich unter dem Cursor befindet, und bei einem Fernschreiber würde man nicht nach oben gehen, da dies wahrscheinlich einen Papierstau verursacht hätte). Daher mussten hierfür Escape-Sequenzen eingeführt werden, bei den meisten Terminals 0x1b 0x5b 0x43 bzw. 0x1b 0x5b 0x41 (das ist übrigens auch die Byte-Sequenz, die viele Terminals beim Drücken der Tasten Rightund senden Up, sofern diese über entsprechende Tasten verfügen).
Zu den von Terminals unterstützten Escape-Sequenzen gehören einige, die:
- Ändern Sie die Text- oder Hintergrundfarbe und andere grafische Darstellungsattribute
- den Zeichensatz ändern. Beispielsweise gibt es in Latin1 kein griechisches Zeichen, und Terminals (aus der Zeit vor Unicode und auch heute noch) unterstützen das Umschalten auf einen anderen Zeichensatz, um Buchstaben anderer Sprachen oder Box-Zeichen anzuzeigen.
- die Position von Tabulatorstopps festlegen
- kann Informationen vom Terminal abfragen wie z.B. Cursorposition, Farbe, Fenstertitel, Größe...
- kann sich darauf auswirken, wie Eingaben verarbeitet werden. Einige Terminals unterstützen beispielsweise den Wechsel in einen Modus, in dem beim Drücken von Shift+ Abeispielsweise kein 0x41-
A
Zeichen (ASCII) gesendet wird, sondern eine Bytefolge, die Informationen über Modifikatoren (Umschalt, Alt, Strg...) und Tastencodes kodiert. - Einige X11-Terminalemulatoren erkennen Escape-Sequenzen, um die Schriftart oder Fenstergröße zu ändern, JPEG-Bilder anzuzeigen oder den Bildschirminhalt an einen Drucker zu senden …
In einer Textdatei gibt es normalerweise nur Bytes (oder Bytefolgen bei UTF-8 oder anderen Multibyte-Zeichensätzen), die grafische Zeichen darstellen. Die einzigenKontrolleZeichen, die Sie in Textdateien finden, sind NL (0xa, auch bekannt als LF) und TAB (0x9).
Wenn Sie dies tun cat file.txt
, cat
liest einfach den Inhalt von file.txt
und schreibt ihn in seine Standardausgabe. Wenn die Standardausgabe eine serielle oder Pseudo-TTY-Gerätedatei ist ( z. B. ) /dev/ttyS0
, /dev/pts/0
auf die eine Terminalzeilendisziplin übertragen wurde, wie dies der Fall wäre, wenn Sie diesen Befehl von einer interaktiven Shell in einem Terminalemulator ausführen, übersetzt die Zeilendisziplin diese NLs in CR+NL (obwohl NLNL möglicherweise nur in CRNLNL übersetzt wird), sodass das Terminal beim Empfang von CRNL den Cursor an den Anfang und dann nach unten bewegt.
Der Text im Inhalt der Datei wird also auf dem Terminalbildschirm angezeigt, sofern der Text in der Datei im Zeichensatz des Terminals codiert ist.
Nun sind Bytes in ausführbaren Dateien oder anderen zufälligen Binärdateien nicht dazu gedacht, Zeichen darzustellen, sie können beliebige Werte annehmen, auch Werte im Bereich von 0 bis 31. Wenn sie also an ein Terminal gesendet werden, wird das Terminal tun, was ihm gesagt wird, und sie als Steuerzeichen interpretieren, was dazu führen kann, dass es alles wie oben aufgelistet und noch viel mehr tut und es dadurch völlig unbrauchbar wird.
Um dem vorzubeugen, senden Sie diese Dateien zunächst nicht an ein Terminal, da dies keinen Sinn ergeben würde, oder wenn Sie nicht wissen, ob es sich bei einer Datei um eine Textdatei handelt (oder um eine Datei, die von einem Terminal mit Escape-Sequenzen wörtlich angezeigt werden soll).beabsichtigtvon einem Terminal interpretiert werden) oder nicht, können Sie ein Tool verwenden, das entweder die Steuerzeichen entfernt (zumindest alle außer TAB und NL) oder ihnen eine visuelle grafische Darstellung gibt.
Dies ist, was die Optionen -v
und tun -t
, wie sie von vielen cat
Implementierungen unterstützt werden. Wobei mit -v
alles außer NL und TAB in eine ^X
Notation für Bytes 0 bis 31 und 0x7f, M-^X
für Bytes 0x80 bis 0x9f und 0xff und M-X
für Bytes 0xa0 bis 0xfe konvertiert wird, die gängige visuelle Darstellungen von Nicht-ASCII-Zeichen sind. Und -t
tut dies nur für TAB (geändert in ^I
).
Oder Sie verwenden einen Pager wie less
oder vim
, view
die dies standardmäßig tun (zumindest, solange Sie die Optionen -r
/ -R
raw nicht verwenden) und etwas intelligenter sind, da sie keine Nicht-ASCII-Zeichen transformieren, die in Ihrer Region grafisch dargestellt werden sollen, und durch die Verwendung von Farb- oder Hervorhebungsmodi deutlicher machen, welche Bytes transformiert wurden.
Oder Sie verwenden spezielle Tools zur Vorschau von Nicht-Textdateien wie hexdump -C
oder xxd
.
Siehe auch den l
Befehl von sed
, der etwas Ähnliches tut cat -vte
und (im Gegensatz zu ) Standard ist, jedoch cat -vte
auf weniger mehrdeutige Weise:
sed -n l < a-file
Antwort4
- Ja, junger Padawan.
$ cargo search bat
bat = "0.23.0" # A cat(1) clone with wings.
- Aber Meister, was ist eine Fracht?
$ cargo --help |any install rust
Rust's package manager
--list List installed commands
--explain <CODE> Run `rustc --explain CODE`
install Install a Rust binary. Default location is $HOME/.cargo/bin
uninstall Uninstall a Rust binary
$ cargo install bat
(...)
Meister:Dies passiert, wenn Sie versuchen, bat
eine Binärdatei zu verwenden.
$ bat `which bat`
[bat warning]: Binary content from file '/home/jaroslav/.cargo/bin/bat'
will not be printed to the terminal (but will be present if the output
of 'bat' is piped). You can use 'bat -A' to show the binary file contents.
- Padawan:Gibt es beim Schlagen irgendwelche Nachteile, Meister?
- Meister:Ja, es kann bei großen Dateien langsam sein, aber das liegt teilweise daran, dass es strukturierte Syntax hervorhebt. Das kann deaktiviert werden, z. B. --style=plain --color=never
- Padawan:Was ist mit den seltsamen Zeichen, die cat dann an das Terminal ausgibt, Master?
- Meister:Das liegt daran, dass Terminals alles, was wie ein ANSI-Escape-Code aussieht (interner Terminalbefehl), gerne akzeptieren und interpretieren und versuchen, das auszuführen, was der Befehl sagt, wenn dieser Befehl implementiert ist. Eine kurze Einführung finden Sie hierListe der Ansi-Farb-Escape-Sequenzen
So können Sie dieses Verhalten reproduzieren:
$ echo -ne "\u1B\u5B\u63" | xxd
00000000: 1b5b 63 .[c
$ echo -ne "\u1B\u5B\u63"
^[[?1;2c
$ 1;2c