安全な猫はいますか?

安全な猫はいますか?

時々、curlまたはローカルファイルシステムからバイナリをcatすることがあります。ほとんどの場合、壊れたターミナルは次のように修正できます。リセットその他の場合、特にバイナリが大きい場合は、ターミナルが数分間停止し、次のような出力が表示されます。

2c1

別名

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;

このシナリオに関して 3 つの質問があります。

  1. 2c1 は何を意味し、なぜ端末がこれを出力するのでしょうか?
  2. cat対話型セッションでこのような望ましくない動作を防ぐために、実際にを見たことがありますか?
  3. このような猫をプログラムする方法について何か提案はありますか(CEEまたはGolangで)

私の最初の直感は、これを検出するために関数で cat をラップすることでしたが、すぐにそれを正しく行うことはかなり難しく、多数のエッジケースが発生することに気付きました。

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
}

実用的な解決策としては、「安全でない」入力を調べるときに常に less (LESSPIPE を使用) を使用することですが、この質問はページャーに関するものではありません。less と lesspipe については知っています。毎日積極的に使用しています。おそらく less+lesspipeこの問題の解決策は、lessの著者が20〜30年前に同じ問題に直面して実装したものです。

しかし、cat は「ポケベル」とは複数の点で異なります... 第一に、cat は非対話型です。これは私にとって重要です。

less+lesspipe に関する提案は、実用的には(私見では)本当に良いのですが、私は制御文字、特殊なエスケープ シーケンス、およびさまざまな端末がこれらの入力をどのように処理するかという細かい点にもっと関心があります。

私は、制御文字の技術的な詳細と、端末やシェルが「ゴミ」や制御文字をどのように解釈するかに興味があります。「この問題をどのように解決するか」を尋ねているのではありません。「なぜ端末はバイナリ ファイルをこのように処理するのか」を尋ねているのです。

答え1

less代わりに、バイナリファイルについて警告し、いくつかのシステムではさまざまな種類のファイルを処理できるものを使用します(例えばless file.rpmCentOS 7 では、 RPM 内のファイルを実行して表示できます。これは「lesspipe」と呼ばれるものだと思います。

また、次回同じことが起きる場合は、resetまたは を試してtput reset正常に戻すことができます。これらは、デフォルトの正常な状態にリセットするように端末に指示するエスケープ シーケンスを送信し、 と同等の処理を行ってstty sanetty デバイス ファイルの設定を正常なデフォルトに変更します (ただし、バイナリ ファイルをダンプしても、これらには影響しません)。また、は、照会をサポートする端末で、reset端末または端末エミュレーターのウィンドウ サイズに関する tty デバイス ファイルの概念 ( によって報告される) を修正することもできます。tty size

答え2

誰も「文字列」について言及していません。文字列は cat とまったく同じではありませんが、連続したテキスト データからテキスト文字列だけを印刷して、ターミナルで安全に表示できるようにします。通常は binutils パッケージに付属しています。これは、印刷されたデータからバイナリ出力が得られないことをすばやく確認するための便利なプログラムであり、連続した非バイナリのみを表示したい場合にも役立ちます。デフォルトでは、4 文字以上の ASCII 文字を含む連続したテキスト セクションのみを印刷することに注意してください。これは -n オプションで調整できます。

答え3

シリアル ラインまたは疑似 tty デバイス (シリアル ラインをエミュレートする) を介して、端末または端末エミュレーターと対話します。

カーネルには、一種の適応層として中間に配置され、何らかの変換を行うソフトウェア モジュールがありますが (後ほど簡単に説明します)、通常は次のようになります。

  • シリアルラインを介して端末にバイトストリームを送信し、端末はそれを画面に表示するグリフまたは動作を変更するための特別な命令として解釈します。
  • 代わりに、端末はシリアル ライン上の別のワイヤを介してバイト ストリームをコンピュータに送信し、入力内容をコンピュータに伝えたり、受信した制御クエリの一部に応答したりします。

たとえば、端末を ISO8859-1 (別名 latin1 端末) として構成すると、0x53 0x74 0xe9 0x70 0x68 0x61 0x6e 0x65 を受信すると、画面上の現在のカーソル位置にS、、、、、、、、、グリフtをレンダリングすると解釈します。逆に、ユーザーが と入力すると、端末は0x53バイトを送信しますéphaneS

0から0x1fの範囲のバイト値は次のように解釈されます。コントロール文字です。つまり、グリフとして表現されるのではなく、特別な意味を持ちます。

例えば:

  • 0x7 (BEL) 音声またはビデオアラートを生成する
  • 0x8 (BS) カーソルを左に移動する
  • 0xa (LF) カーソルを下へ移動
  • 0xd (CR) はカーソルを画面の最初の列に移動する
  • 0x9 (TAB) はカーソルを次のタブに移動する

この範囲には32個の制御文字しかありませんが、ほとんどの端末にはさらに多くの機能や制御方法があります。そのため、これらの文字以外にも、1つ端末を制御するためのバイト。ほとんどの端末とほとんどのシーケンスでは、最初のバイトは 0x1b (ESC) で、その後に 1 つ以上のバイトが続きます。

たとえば、上記のようにカーソルを左または下に移動するための制御文字はありますが、右または上に移動するための制御文字はありません (元々、テレタイプライターでは、右に移動するには「スペース」を使用しますが、CRT 端末ではカーソルの下にあるものを消去します。また、テレタイプライターでは、紙詰まりの原因となるため、上に移動することはありません)。そのため、これらのためにエスケープ シーケンスを導入する必要がありました。ほとんどの端末では、それぞれ 0x1b 0x5b 0x43 と 0x1b 0x5b 0x41 です (ちなみに、これは、そのようなキーがある端末でRightとが押されたときに多くの端末が送信するバイト シーケンスでもありますUp)。

現在、端末がサポートしているエスケープ シーケンスには次のようなものがあります。

  • テキストや背景色、その他のグラフィックレンダリング属性を変更する
  • 文字セットを変更します。たとえば、latin1 にはギリシャ文字が存在せず、端末 (Unicode 以前の時代から現在まで) は、他の言語の文字やボックス描画文字を表示するために別の文字セットに切り替えることをサポートしています。
  • タブストップの位置を設定する
  • カーソルの位置、色、ウィンドウのタイトル、サイズなどの情報を端末から照会できます。
  • 入力の処理方法に影響を与える可能性があります。たとえば、一部の端末では、Shift+を押すとA0x41 (ASCII A) 文字ではなく、修飾子 (shift、alt、ctrl...) とキーコードに関する情報をエンコードしたバイト シーケンスを送信するモードに入ることがサポートされています。
  • 一部の X11 ターミナル エミュレーターは、フォントやウィンドウ サイズを変更したり、JPEG 画像を表示したり、画面の内容をプリンターに送信したりするためのエスケープ シーケンスを認識します。

テキストファイルでは、通常、グラフィック文字を表すバイト(UTF-8やその他のマルチバイト文字セットの場合はバイトシーケンス)のみが含まれます。コントロールテキスト ファイルで見つかる文字は、NL (0xa、別名 LF) と TAB (0x9) です。

を実行するとcat file.txtcatは の内容を読み取りfile.txt、それを stdout に書き込みます。stdout がシリアルまたは疑似 tty デバイス ファイル ( など) であり、/dev/ttyS0その/dev/pts/0コマンドを端末エミュレータの対話型シェルから実行する場合のように、端末の回線制御規則がプッシュされている場合、回線制御規則はそれらの NL を CR+NL に変換します (ただし、NLNL は CRNLNL のみに変換される可能性があります)。そのため、端末は CRNL を受信すると、カーソルを先頭に移動してから下に移動します。

したがって、ファイル内のテキストが端末の文字セットでエンコードされている場合、ファイルの内容のテキストは端末画面に表示されます。

現在、実行可能ファイルやその他のランダム バイナリ ファイル内のバイトは文字を表すためのものではなく、0 から 31 までの範囲の値を含む任意の値を持つことができるため、端末に送信すると、端末は指示に従ってそれらを制御文字として解釈し、上記にリストされているようなことやそれ以上のことを実行し、完全に使用できなくなる可能性があります。

それを防ぐには、まず、それらのファイルを端末に送信しないようにします。それは意味がありません。または、ファイルがテキストファイルであるかどうかわからない場合(または、エスケープシーケンスを使用して端末でそのまま表示することを意図したファイルである場合)意図されました制御文字 (少なくとも TAB と NL 以外のすべて) を削除するか、制御文字を視覚的にグラフィカルに表現するツールを使用できます。

これが、多くの実装でサポートされている-vおよびオプションが行うことです。 を使用する場合、NL と TAB を除くすべての文字は、非 ASCII 文字の一般的な視覚的表現である、バイト 0 ~ 31 と 0x7f、バイト 0x80 ~ 0x9f と 0xff、バイト 0xa0 ~ 0xfe については何らかの表記に変換されます。 および は、TAB に対してのみこれを行います ( に変更されます)。-tcat-v^XM-^XM-X-t^I

lessまたは、やvimなどのページャーを使用することもできます。これらはデフォルトでこれを実行し (少なくとも/ raw オプションviewを使用しない限り)、ロケールでグラフィカルに表現されることを意図した非 ASCII 文字を変換せず、色付けや目立つモードを使用してどのバイトが変換されたかを明確にする点で少しスマートです。-r-R

hexdump -Cまたは、やなどのテキスト以外のファイルのプレビュー専用のツールを使用することもできますxxd

lのコマンドも参照してください。sedこれは と似た動作をしますがcat -vte、 は とは逆にcat -vte、より曖昧さが少ない標準的な方法です。

sed -n l < a-file

答え4

  • はい、若きパダワン。
$ cargo search bat 
bat = "0.23.0"            # A cat(1) clone with wings.
  • しかし、マスター、貨物とは何ですか?
$ 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
(...)

マスター:batバイナリを試みると、これが発生します。

$ 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.
  • パダワン:バットには何か欠点がありますか、マスター?
  • マスター:はい、大きなファイルでは遅くなる可能性がありますが、これは部分的には構造化された構文の強調表示を行うためです。この機能は無効にできます。例: --style=plain --color=never
  • パダワン:では、cat が端末に出力する奇妙な文字についてはどうでしょうか、マスター?
  • マスター:これは、端末がANSIエスケープコード(内部端末コマンド)のように見えるものをすべて喜んで受け入れて解釈し、そのコマンドが実装されている場合は、そのコマンドが指示するとおりに動作しようとするためです。簡単な紹介については、こちらをご覧ください。ANSI カラー エスケープ シーケンスのリスト

この動作を再現する方法は次のとおりです。

$ echo -ne "\u1B\u5B\u63" | xxd
00000000: 1b5b 63                                  .[c

$ echo -ne "\u1B\u5B\u63" 
^[[?1;2c

$ 1;2c

関連情報