web-dev-qa-db-ja.com

端末エスケープシーケンス:端末がterminfoに依存せずに、サポートする機能を端末に報告しないのはなぜですか?

私は最近、エスケープシーケンスを調査していますが、エスケープシーケンスの機能に驚いています。一緒にxterm X11ウィンドウを移動することもできます(printf '\e[3;0;0t'を試してください)。

端末がサポートする機能を知る最も一般的な方法は、データベースを使用しているようです。これがncursesが行うことであり、エスケープシーケンスに依存する99.9%のアプリケーションで使用されます。
Ncursesは、コンソールでサポートされている機能を判断するために、シェルのterminfo環境変数のTERMデータベースを読み取ります。
シェルのTERM環境変数を変更できます。変更すると、ほとんどのアプリケーションで機能の使用が少なくなったり、誤動作したりする可能性があります(TERM=""の設定後にnanoまたはvimを実行してください)。

いくつかのエスケープコードにより、端末が何かを報告することを確認しました。たとえば、<ESC>[6nは、端末にカーソル位置を報告させます。 (printf '\e[6n'

  • 同様のレポートメカニズムを使用して、コンソールがサポートする機能をコンソールに報告させないのはなぜですか?

機能をTERMの値と結合する代わりに、各コンソールが独自の機能をアドバタイズして、全体をより正確で信頼できるものにすることができます。なんでこれじゃないの?


編集:私が前に尋ねるべきだったもの...新しいエスケープシーケンスを作成し、それをサポートし、いくつかのスクリプトで使用するために、konsoleとgnome-terminalをハッキングします。
実行しているコンソールがこの機能をサポートしているかどうかを確認するために、コンソールにクエリを実行できるようにしたいのですが、そのために推奨される理由は何ですか?

6
peoro

想像するほど簡単ではありません。 xterm(VT100で始まるDEC VTxxx端末のような)には、さまざまな機能のレポートが多数あります( を参照してください)XTerm制御シーケンス )。最も一般的に役立つのは、端末のタイプを通知するものです。

CSI Ps c  Send Device Attributes (Primary DA).

すべての端末にそのタイプの応答があるわけではありません(Sunハードウェアコンソールはnoneを持っています/持っていません)。

しかし、レポートよりも多くの機能があります(たとえば、端末が実際にUTF-8を解釈しているかどうかを確認する方法:そのための受け入れられるルートは、locale環境変数を経由するため、いいえ別の制御シーケンス/応答の必要性が確立されています)。

実際には、レポートに注意を払うアプリケーションがいくつかありますが(vimなど)、ファンクションキーの実際の値、DCS + p Pt ST、さらにを使用したカーソルの外観もDCS $ q Pt ST)、プロセスを信頼できません。一部の開発者は、機能を実装するよりも特定のレポート応答を返す方が簡単であると考えています。さまざまなプログラムのソースコードを読むと、誰かが応答をカスタマイズして、xtermのいくつかのバージョンのように見えるという興味深い癖があります。

7
Thomas Dickey

端末エミュレーターを照会するエスケープシーケンスについての私の見解を示します。

tl; dr:非同期という性質上、アプリでの処理は本当に問題があります。 (ターミナルエミュレータではなく、簡単なことです。)


まず、簡単にするために、すべての端末エミュレーターがそのようなすべてのクエリに応答を送信することが保証されていると仮定しましょう。 (これには、クエリが1回限りのエスケープシーケンスではなく、明確に定義された一般的な構造を持つ必要がありますが、そうではありません)。

簡単なユーティリティ( "ls"など)とより複雑なフルスクリーンアプリ( "mc"や "vim"など)を設計して実装し、私たちが直面している問題を見てみましょう。

  • Unixの標準機能は、前のコマンドの実行中に次のコマンドの前に入力できることです(たとえば、「sleep 10」Enter、次に「mc」Enterと入力し、F5キーを押します。約10秒後に「mc」が開きます。コピーダイアログ)。この機能が好きかどうかはわかりませんが、少なくとも動作はアプリ間で一貫している必要があります。これは、ターミナルエミュレータに動的にクエリを実行しないアプリの動作です。また、「mc」が起動時にこのようなクエリエスケープシーケンスを使用して、どちらの機能も把握しているとします。これで、mcは応答の前にF5のエスケープシーケンスを受け取ります。それを無視するか(その場合、動作は他のアプリと矛盾します)、どこかに隠しておき、クエリへの応答が到着するまで待ってから、このF5を処理する必要があります。これを行うには、各コンポーネントにstdinから直接読み取って処理させることができなくなり、間にラッパーレイヤーが必要になります。実行可能ですが、実装にはかなりの労力を要します(必要な追加作業は、この基準なしで既に実装されているものはもちろん、ゼロから開始したプロジェクトでも顕著です。したがって、大幅にリファクタリングする必要があります)。

  • 動作中になんらかの理由で端末に問い合わせる必要がある場合も同様です。次に、応答が到着する前に中間の「通常の」文字をドロップすることは絶対に受け入れられません。

  • ここで、Cまたは類似の言語で記述されたユーティリティの代わりに、この応答とユーザーからの他の通常の入力(stdinから)を読み取る必要があるシェルスクリプト自体を想像してみてください。どうしますか?このような応答を処理するコードの各「読み取り」で、ターミネーター文字を手動で改行または応答のエスケープシーケンスの終わりに設定し、残りのエスケープシーケンスを見つけて取り除きますか?ユーザー入力がまだ表示されているときに、エスケープシーケンス応答が画面に表示されないようにする方法を教えてください。ユーザーデータを期待する後続の「通常の」「読み取り」コマンドへのエスケープシーケンス応答を期待しながら受け取る通常の入力をどのように「フィード」しますか?どうすればよいのかわかりませんが、たとえできたとしても、明らかに耐え難いほど複雑で、面倒でエラーが発生しやすいものです。合理的に実装可能で確実に機能していると私が想像できる唯一のことは、先行入力文字をドロップし(異常な動作を引き起こす)、スクリプトの起動時にのみそのような応答エスケープシーケンスを処理することです。

  • ループ内のシェルスクリプトで単純なユーティリティ(「ls」など)を使用すると、ターミナルエミュレーターとアプリの間の往復時間が長くなる可能性があります。シングルコアシステムでは、2つのアプリ(ユーティリティとターミナルエミュレーター)間のコンテキストスイッチが必要ですが、とにかく起こるfork()+ execve()+ friendsに比べてそれほど悪くはないでしょう。マルチコアシステムでは、これは必要ないと思いますが、詳細はわかりません。ただし、実際のネットワークトラフィックが関係している場合、コスト(レイテンシ)は非常に大きくなる可能性があります。

  • アプリが応答を読み取らずに終了するまれなケース(たとえば、クラッシュまたは強制終了)では、入力を開始した次のコマンドに応答が表示されます(これは、偶然に猫を飼ったときなど、すでに起こっていることを確認しています)バイナリファイル)。


ここで、将来のエスケープシーケンスを含め、一部のクエリエスケープシーケンスを認識しない(または応答しないことを選択した)端末エミュレータがあると仮定します。これが現在の状態です。これにより、これまでのすべての箇条書きがさらに困難になります。この場合、アプリがフリーズするリスクはないため、タイムアウトが必要です。

  • 答えをどのくらい待ちますか?どのようにして任意のタイムアウトを構成しますか?あなたは(そして、どのようにして)ネットワークの特性に従ってこのタイムアウトを調整しますか(たとえば、ローカルターミナルエミュレーターvs近隣の建物へのssh対地球の反対側へのssh)?

  • 応答が時間内に到着しない場合(たとえば、sshの遅延が原因)アプリは、ユーザーに表示される低下モードで続行しますか?

  • アプリが断念するよりも遅れて応答が届いた場合はどうなりますか?アプリは、そのような応答がまったく予期しない場所でも届くように準備する必要がある可能性があります。 (たとえば、シェルスクリプトで最初にいくつかの状態をクエリし、タイムアウトで応答を待ちますが、後ですべての「読み取り」はすべて、この遅延応答で汚染されるように準備する必要があります。)

  • タイムアウトにより、ラウンドトリップ時間が大幅に増加します。これは、おそらくカーネルコンテキストスイッチやネットワーク遅延よりもはるかに長くなります。このようなコマンドをシェルスクリプトループに入れることは絶対に耐えられませんが、おそらくインタラクティブアプリの使いやすさへの否定的な影響でさえ目立つでしょう。


どのような代替案が実現可能かを示すことは、この回答の範囲を超えています。 TERM変数の設計にも多くの制限がありますが、ここでは説明しません。クエリ機能(カーソル位置などの現在のプロパティではなく、ターミナルエミュレーターでは静的)については、実際の動作が説明されているTERMCAPの方向から開始する可能性があります。ローカルファイルを指すこともでき、現在のようにTERMと呼ばれることもありますが、sshのようなユーティリティは、.Xauthorityと同様に、実際のコンテンツをリモートサイトに転送し、そこからTERMをこのファイルにポイントする必要があります。 。別の完全に異なるアプローチは、端末エミュレーターとのそのようなメタ通信のための4番目の標準ファイル記述子を持つことです。

4
egmont