5

端末エミュレーターには行と行の概念があるようですが、それについてもっと知りたいです。

行と行の意味のデモンストレーション

以下の Python スクリプトは、3 行の「a」を表示して待機し、次に 3 行の「b」を表示します。

import sys, struct, fcntl, termios

write = sys.stdout.write
def clear_screen(): write('\x1b[2J')
def move_cursor(row, col): write('\x1b['+str(row)+';'+str(col)+'H')
def current_width(): #taken from blessings so this example doesn't have dependencies
    return struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '\000' * 8))[1]

clear_screen()
for c in 'ab':
    #clear_screen between loops changes this behavior
    width = current_width()
    move_cursor(5, 1)
    write(c*width+'\n')
    move_cursor(6, 1)
    write(c*width+'\n')
    move_cursor(7, 1)
    write(c*width+'\n')

    sys.stdout.flush()
    try: input() # pause and wait for ENTER in python 2 and 3
    except: pass

ここに画像の説明を入力

この休憩中に端末ウィンドウの幅を 1 文字狭くすると、次のように表示されます。

ここに画像の説明を入力

これはかなり妥当なようです。各行は個別にラップされています。もう一度エンターを押してbs を印刷すると、

ここに画像の説明を入力

すべてが期待どおりに機能します。私は絶対カーソル位置を使用し、以前に書き込んだのと同じ行に書き込みました - もちろん、すべての a を上書きするわけではありません。それらの多くは他の行にあるからです。

ただし、ウィンドウをさらに 1 文字狭めると、ラッピングの動作が異なります。

ここに画像の説明を入力

2 番目と 3 番目の行がb折り返されたのはなぜaですか? また、' の最後の行が b の最初の行と結合したのはなぜですか? 上の表示されている行に理由のヒントがあります - 2 つaの が表示されているのは、これらの 2 つの行がまだリンクされているためです - もちろん、ウィンドウを再度移動すると、その 1 つの行は同じように折り返されます。これは、行全体を置き換えた行でも発生しているようです。

以前に折り返されていた行が、対応する親行にリンクされていることがわかります。ターミナルを大きく広げると、それらが同じ論理行に属していることがより明白になります。

ここに画像の説明を入力

私の質問

実際には、私の質問は、この行が行に結合されるのをどのように防止または予測するかです。画面全体をクリアするとこの動作はなくなりますが、アプリケーションを大幅に高速化する行ごとのキャッシュを保持できるように、可能であればこれを必要とする個々の行に対してのみこれを行うとよいでしょう。行の最後までクリアすると、その行はその下の行からリンク解除されますが、行の先頭までクリアしても、その行からその行のリンクは解除されません。

私は興味があります - これらの行のものは何ですか? それらについてどこで読むことができますか?どの行が同じ行の一部であるかを調べることはできますか?

私は、tmux の有無にかかわらず、terminal.app と iterm でこの動作を観察しました。仕様がなくても、これらのいずれかにソースダイブすると答えが得られると思いますが、どこかに仕様があると思います!


背景: ユーザーがウィンドウの幅を狭めた場合に端末の折り返しが発生する方法を予測できる端末ユーザー インターフェイスを作成したいと考えています。tput smcup行の折り返しを防ぐために機能するフルスクリーンモード( 、または'、ncursesが使用するもの)のようなものは知ってpython -c 'print "\x1b[?1049h"いますが、ここでは使用したくありません。

編集:スクリプトの上書き動作をすでに理解していることをより明確にし、ラッピング動作の説明が必要です。

4

2 に答える 2

7

わかった。表示されている動作の原因から始めましょう。

コードをテストしたところ、ウィンドウのサイズを変更したときにのみ発生することがわかりました。ウィンドウをそのままにしておくと、a が書き出され、Enter キーを押すと b で上書きされます (意図した動作だと思います)。

途中でウィンドウのサイズを変更すると、行のインデックスが変更されるため、次の反復で move_cursor() を呼び出すときに同じ座標を信頼できないように見えます。

興味深いことに、ウィンドウのサイズを変更すると、ワード ラップによってカーソルの前のテキストが上に押し上げられます。これは端末エミュレーターのコードの一部だと思います (ほとんどの場合、カーソルにフォーカスを保持したいので、カーソルが画面の下部にある場合、ワードラップによって下に押された場合、サイズを変更するとウィンドウの高さを超えてカーソルが見えなくなる可能性があるため) )。

Enter キーを押してサイズを変更すると、a の 2 行だけが表示されたままになります (3 行すべてではありません)。何が起こっているように見えるかは次のとおりです。

まず、初期出力から始めます。(わかりやすくするために行番号を追加)

1
2
3
4
5 aaaaaaaaaaaaaaa\n
6 aaaaaaaaaaaaaaa\n
7 aaaaaaaaaaaaaaa\n
8 

これらの各行の終わりに改行文字があることに注意してください (これが、カーソルを再度移動していないにもかかわらず、カーソルが最後の行の下に表示される理由です)。

ウィンドウを 1 文字縮小すると、次のようになります。

1
2 aaaaaaaaaaaaaa
3 a\n
4 aaaaaaaaaaaaaa
5 a\n
6 aaaaaaaaaaaaaa
7 a\n
8

「テキストを上に押し上げる」という言葉の意味に気付くでしょう。

Enter キーを押してループを繰り返すと、カーソルは (コードで指定されているように) 行 5 列 1 に送られ、2 行目の最後の a の上に直接配置されます。b の書き込みを開始すると、2 行目の最後の a が b で上書きされ、その後の行も同様に上書きされます。

1
2 aaaaaaaaaaaaaa
3 a\n
4 aaaaaaaaaaaaaa
5 bbbbbbbbbbbbbb\n
6 bbbbbbbbbbbbbb
7 bbbbbbbbbbbbbb\n
8

重要なことに、これにより、a の 2 行目の末尾にある改行文字も上書きされます。これは、a の 2 行目と b の 1 行目を分割する改行がないことを意味するため、ウィンドウを展開すると、これらは 1 行として表示されます。

1
2
3 
4
5 aaaaaaaaaaaaaaa\n
6 aaaaaaaaaaaaaabbbbbbbbbbbbbb\n
7 bbbbbbbbbbbbbbbbbbbbbbbbbbbb\n
8

この b の 2 行目も一緒にされる理由は完全にはわかりませんが、最初の行が上書きする a の行に、それ自体の改行終了がないという事実と関係があるようです。ただし、それは単なる推測です。

ウィンドウをさらに別の文字で縮小しようとすると、2 文字の行折り返しが発生する理由は、同じテキスト行の 2 つの半分を縮小しているためです。最後に1つ。

例: 私が示したこれらのテスト ウィンドウでは、幅は 15 文字から始まり、14 文字に縮小して b を出力します。15 文字の長さの a の行がまだ 1 行あり、14 文字で改行された 14 の a と 14 の b の行があります。同じことが (何らかの理由で) b の最後の 2 行にも当てはまります (これらは 14 でラップされた 28 文字の 1 行です)。したがって、ウィンドウをもう 1 文字 (13 まで) 縮小すると、15 個の a の最初の行に 2 つの末尾文字 (15 - 13 = 2) が表示されます。28 文字の次の行は、13 文字幅のウィンドウ (28 / 13 = 2 R2) に収まる必要があり、最後の b にも同じことが当てはまります。

0 aaaaaaaaaaaaa
1 aa\n
2 aaaaaaaaaaaaa
3 abbbbbbbbbbbb
4 bb\n
5 bbbbbbbbbbbbb
6 bbbbbbbbbbbbb
7 bb\n
8

なぜこのように機能するのですか?:

この種のものは、テキストを適切に再配置する機能を持つ別のプログラム内でプログラムを実行しようとするときに遭遇する困難です。サイズを変更すると、インデックスが信頼できなくなります。端末エミュレーターは再調整を処理しようとしており、アクティブなプロンプトが常に表示されるように、プロンプト (行 8 で固定) の前のテキストをスクロールバックで上下にプッシュしています。

行と列は端末/端末エミュレーターによって定義されるものであり、それに応じてそれらの場所を解釈するのはそれ次第です。適切な制御シーケンスが与えられると、適切な表示のためにそれらを解釈するのは端末です。

一部の端末は異なる動作をすることに注意してください。エミュレートされた端末では、エミュレートする端末の種類を変更する設定がよくあります。これは、特定のエスケープ シーケンスの応答方法にも影響を与える可能性があります。これが、UNIX 環境が通常、どのタイプの端末と通信しているかを通知する設定または環境変数 ($TERM) を持っている理由です。これにより、送信する制御シーケンスを知ることができます。

ほとんどの端末は、標準 ANSI 準拠の制御シーケンス、またはハードウェア端末の DEC VT シリーズに基づくシステムを使用します。

Preferences->Settings->Advanced の下の Terminal.app 設定では、「Declare terminal as:」の横にあるドロップダウン メニューで、ウィンドウによってエミュレートされているターミナルのタイプを実際に確認 (または変更) できます。

これを克服する方法:

最後の既知の幅を保存し、変更があったかどうかを確認することで、これを軽減できる場合があります。その場合、カーソルのロジックを変更して、変更を補うことができます。

または、サイズ変更後に前の行を誤って上書きしないように、(絶対ではなく) 相対的なカーソル移動用に設計されたエスケープ シーケンスの使用を検討することもできます。エスケープ シーケンスのみを使用して、特定のカーソル位置を保存および復元する機能もあります。

Esc[<value>A  Up
Esc[<value>B  Down
Esc[<value>C  Forward
Esc[<value>D  Backward
Esc[s         Save Current Position
Esc[u         Restore Last Saved Position
Esc[K         Erase from cursor position to end of line

ただし、すべてのターミナルエミュレーターがウィンドウのサイズ変更を同じ方法で処理するという実際の保証はありません (これは、実際にはターミナル標準の一部ではありませんが、AFAIK)、または将来変更されないという保証はありません。真のターミナル エミュレーターを作成したい場合は、最初に GUI ウィンドウをセットアップして、すべてのサイズ変更ロジックを制御できるようにすることをお勧めします。

ただし、ターミナル エミュレーター ウィンドウで実行し、作成中の特定のコマンド ライン ユーティリティのウィンドウのサイズ変更を軽減する必要がある場合。Pythonのcursesライブラリを見ることをお勧めします。これは、私が頭のてっぺんから知っているすべてのウィンドウサイズ変更対応プログラム (vim、yum、irssi) で使用される機能の一種であり、この種の変更を処理できます。私は個人的にそれを使用した経験はありませんが。

cursesモジュールを介してPythonで利用できます。

(また、プログラムを再配布する予定がある場合は、Python3 で作成することを検討してください。子供たちのためにそれを行ってください:D)

資力:

次のリンクが役立つ場合があります。

それが役立つことを願っています!

于 2014-09-09T05:30:44.093 に答える
1

0x783czar が指摘したように、重要な違いは、端末が新しい行を開始する原因となった明示的な改行が出力されたか、または目的の文字を出力するためのスペースが右側に残っていないために暗黙のオーバーフローがあったかです。

コピーと貼り付けの目的 (バッファーに改行文字があるかどうかに関係なく)、多くの端末でのトリプルクリックによるハイライト動作、およびウィンドウがサイズが変更されます (それをサポートする端末で)。

端末内で実行されるアプリケーションは、この違いをほとんど気にせず、「行」と「行」という言葉を同じ意味で使用します。したがって、gnome-terminal でサイズ変更時にコンテンツを再ラップする機能を実装したとき、ターミナルの視覚的な 1 行には「行」または「行」という単語を使用し、隣接する 2 つの改行文字の間のコンテンツには「段落」という単語を使用しました。段落が端末よりも広い場合、段落は複数の行に折り返されます。(これは決して公式の用語ではありませんが、IMO は非常に合理的であり、これらの概念について話すのに役立ちます。)

于 2015-04-22T22:06:28.903 に答える