XTermを起動して下部にメッセージを表示するには?

XTermを起動して下部にメッセージを表示するには?

XTerm を起動すると、端末の最初の行でプロンプトが開始されます。コマンドを実行すると、プロンプトは下部に到達するまで下に移動し、それ以降はそのまま残ります(そうでない場合、またはShiftマウスPage Downがそれを変更する可能性があります)。最終ライフサイクルの始まりを「特別」にする代わりにプロンプトは常に端末の下部にある必要があります。私が持っている点を参考にしてください複数行のプロンプト

もちろん、以前と同じように機能する必要があります(サイズ調整可能、スクロール可能、出力から不要な改行なし、出力が奇妙に消えない)。したがって、同様のPROMPT_COMMAND='echo;echo;...'ものはオプションではありません。理想的には、ソリューションはシェルに固有のものではありません。

編集する:これ現在のソリューション単純なケースを処理するときにいくつかの問題があります。

  • それバッシュ固有。理想的なソリューションは、他のシェルに移植可能でなければなりません。
  • それ他のプロセスによって修正されると失敗します。PS1。たとえば、virtualenvがあります(virtualenv)スタートPS1そして常にスクロールせずに見える部分の上から消えます。
  • Ctrll-今削除歴史の最後のページ。

XTermを分岐する以外に、これらの問題を回避する方法はありますか?

答え1

使用している場合は、bash次のトリックを実行する必要があります。

TOLASTLINE=$(tput cup "$LINES")
PS1="\[$TOLASTLINE\]$PS1"

tputまたは(各プロンプトの前にコマンドを実行しますが、ターミナルウィンドウのサイズを変更した後はそれほど効率的ではありません):

PS1='\[$(tput cup "$LINES")\]'$PS1

終了コードが変更されないようにするには、tput明示的に保存してリセットするだけです。

PS1='\[$(retval=$?;tput cup "$LINES";exit $retval)\]'$PS1

この変数はローカル変数なので、シェルで定義した変数にはretval影響しません。retval

ほとんどの端末cup機能は同じなので、\e[y;xHハードコードすることもできます。

PS1='\[\e[$LINES;1H\]'$PS1

後でPS1をリセットしないようにするには、このPROMPT_COMMAND変数を使用することもできます。設定するとコマンドで実行されます。今後出力プロンプト。だからこんな効果も得られます

PROMPT_COMMAND='(retval=$?;tput cup "$LINES";exit $retval)'

もちろん、リセットはPS1ここには影響しませんが、他のソフトウェアも変更される可能性がありますPROMPT_COMMAND

答え2

以前の回答を少し単純化して実行する方が簡単だとわかりました。

tput cup $LINES

.bashrcまたはの始めに.zshrc。それはただ仕事を終えることです。

利点:

  • シェルを起動すると一度だけ印刷されます。

欠点:

  • ^Lで画面を消去すると印刷されず、エイリアスも役に立ちませclearclear; tput ...
  • 端末のサイズ変更時に別の場所に移動するように求めるメッセージを表示

答え3

私はしばらくの間\[\033[1000H\]ソリューションに@ Thomas Dickeyのインクルードを使用してきました。PS1

過去数ヶ月の間に問題を数回調査した後、ついに問題を防ぐ小さな変更が見つかりました。 ANSIエスケープシーケンスはに含まれてはいけませんが、from(inまたは他の設定ファイル)PS1に直接印刷する必要があります。stdoutPROMPT_COMMAND~/.bashrc

PROMPT_COMMAND=prompt

prompt() {
    r="${?}"
    printf '\033[1000H'
    return "${r}"
}

printfそしてreturn組み込み関数(参考資料を参照builtins(1))なので、プロセスは作成されません。前のコマンドの終了ステータスはに保存されr、Clearを使用してprintfRestoreを使用しますreturn。すでに使用している場合は、PROMPT_COMMAND単に追加してください。

clear-screenこのソリューションは複数行のコマンドで動作しますが、2つの小さな制限があります。まず、readlineコマンド(デフォルトでバインド)を使用して画面を消去すると、プロンプトが画面のControl-L上部に移動し、2番目に、新しい端末を追加しても保持されます。元の場所にプロンプ​​トが表示されます。どちらの場合も、プロンプトが表示されたら(Enterキーを押すなど)、画面の下部に戻ります(bashが実行されている間)。これは、@phil pirozhkovが提案したPROMPT_COMMAND単一または同様のオープンよりも改善されました。tput cup 1000~/.bashrc

さらなる調査によると、clearプログラムはターミナルエスケープシーケンス(clear | xxdまたは同様のパスを介して観察可能)を直接印刷してBashを再度実行するので、正常に動作することがわかりましたPROMPT_COMMANDclear-screen一方、readlineコマンドを使用すると、readline(Bashで使用)が画面消去要求を傍受して再実行せずに、古いプロンプトを簡単に再表示できますPROMPT_COMMAND\033[1000Hエスケープシーケンスはもはやプロンプト自体にはありませんが、runningの副作用なので、プロンプトPROMPT_COMMANDは元の場所に残ります。

最も一般的な解決策は、PROMPT_COMMANDターミナルのサイズ変更clear-screenまたは実行時に実行されるようにBashをパッチすることです。しかし、私はより簡単な解決策を選びました。つまり、直接印刷するようにコマンドをパッチすることですclear-screen。明確に言えば、Bashのデフォルトソース構成には独自のコピーが含まれていますが、私のシステム(Gentoo Linux)では、デフォルトのBash構成はシステムを使用するものであり、パッチシステムはそれを使用する他のプログラムでも機能します。\033[1000Hstdoutreadlinereadlinereadline

とにかく最も簡単な解決策は、Bashをダウンロードし、patch -p1Bashソースディレクトリに次のパッチを適用することです。

--- a/lib/readline/display.c    2021-12-20 10:00:33.370809888 +0000
+++ b/lib/readline/display.c    2021-12-20 09:59:14.920808045 +0000
@@ -3186,11 +3186,17 @@
   ScreenClear ();
   ScreenSetCursor (0, 0);
 #else
+  static char const cup[]={'\033', '[', '1', '0', '0', '0', 'H'};
+  size_t i;
+
   if (_rl_term_clrpag)
     {
       tputs (_rl_term_clrpag, 1, _rl_output_character_function);
       if (clrscr && _rl_term_clrscroll)
    tputs (_rl_term_clrscroll, 1, _rl_output_character_function);
+
+      for (i=0; i < sizeof(cup); i++)
+   putc (cup[i], rl_outstream);
     }
   else
     rl_crlf ();

このパッチは、Bash 5.1に含まれているreadline 8.1に適用されます。パッチには関数clrscrへの引数が含まれていないため、readline 8.0では機能しませんが、パッチを_rl_clear_screen変更するのは簡単です。

これを行っている間、私は別の改善点を決めました。clear-screen画面の内容を削除するコマンドを使用する代わりに、後で端末のスクロールバックバッファからその内容にアクセスできるように内容を上にスクロールしたいと思いました。これを達成するために、前のパッチの上に次のパッチを適用できます。

--- a/lib/readline/display.c    2021-12-20 23:48:23.253322032 +0000
+++ b/lib/readline/display.c    2021-12-20 23:48:41.813321757 +0000
@@ -3191,6 +3191,15 @@
 
   if (_rl_term_clrpag)
     {
+      if (!clrscr)
+      {
+   for (i=0; i < sizeof(cup); i++)
+     putc (cup[i], rl_outstream);
+
+   for (i=0; i < _rl_screenheight; i++)
+     putc ('\n', rl_outstream);
+      }
+
       tputs (_rl_term_clrpag, 1, _rl_output_character_function);
       if (clrscr && _rl_term_clrscroll)
    tputs (_rl_term_clrscroll, 1, _rl_output_character_function);

これにより、端末とシェルがついに私が望む方法で正確に動作します。残りの唯一の問題は、最初のコマンドを入力する前に端末のサイズを大きくすると、最初のコマンドを入力するまでプロンプトがそのまま残ることです。しかし、これは本質的に問題ではありません。私はここに両方の​​パッチを作成し、CC0パブリックドメインの公開に従ってパブリックドメインにリリースします(誰かがそれを将来のプロジェクトに統合したい場合)。


編集:@ Toby Speightの要求に応じて他の端末に適用できます。の場合のPROMPT_COMMAND出力をtput cup 1000変数に保存し、代わりに~/.bashrc印刷します。\033[1000H@トーマスディキの答え

パッチの場合は、\033[1000H速度のためにエスケープシーケンスを直接挿入しました(したがって、カーソル位置を調整するために1つのエスケープシーケンスのみが印刷されます)。なぜなら私だけを使うからです。同じファイル内の他のreadline関数を使用すると、より移植性の高いソリューションを簡単に取得できます(display.c)、特に_rl_move_vert機能。残念ながら、Bash / readlineは現在の垂直カーソル位置(次の場所に保存されている)を正確に知らないようです。_rl_last_v_pos)ほとんどの状況で

私が知っている限り、起動時に現在のカーソル位置は照会されません。つまり、画面上でプロンプトが始まる場所がわからないことを意味します。関数_rl_move_vert自体が単に下に移動します。putc ('\n', rl_outstream)つまり、カーソルがすでに端末の最後の行にあるときに「下に移動」すると、実際には端末バッファをスクロールします。

これ_rl_clear_screen機能また、まったく更新されません_rl_last_v_pos。つまり、コマンドを使用して画面を消去した後、カーソルがどこにあるかはわかりませんが、コマンドをclear-screen使用してアクセスできます_rl_clear_screen。 readlineは、おそらくエスケープシーケンスを使用するように拡張できます。選択の使用_rl_term_clrpag(画面の消去)やその他の関連エスケープシーケンスと同様に、カーソル位置を移動します.しかし、これがどのように機能するのか、そしてこれらのエスケープシーケンスがどこで初期化されるのかはわかりません。

とにかく、私は_rl_move_vertまだ機能する機能を備えた新しいパッチを作成しました。戻るスクロールは、常にページ全体をスクロールするのではなく、必要な量(好ましくは可能です)だけスクロールするという点で、以前のパッチとは異なります。少なくとも私の考えには次のようになります。

--- a/lib/readline/display.c    2021-12-21 09:57:55.932774006 +0000
+++ b/lib/readline/display.c    2021-12-21 09:19:59.109474783 +0000
@@ -3186,11 +3186,25 @@
   ScreenClear ();
   ScreenSetCursor (0, 0);
 #else
+  int i;
+
   if (_rl_term_clrpag)
     {
+      if (!clrscr)
+      {
+   i = _rl_screenheight-_rl_last_v_pos;
+   _rl_move_vert(_rl_screenheight);
+
+   for (; i < _rl_screenheight; i++)
+     putc ('\n', rl_outstream);
+      }
+
       tputs (_rl_term_clrpag, 1, _rl_output_character_function);
       if (clrscr && _rl_term_clrscroll)
    tputs (_rl_term_clrscroll, 1, _rl_output_character_function);
+
+      _rl_last_v_pos = 0;
+      _rl_move_vert(_rl_screenheight);
     }
   else
     rl_crlf ();

答え4

使用された回答は$LINES不必要に移植性がありません。完了resize、簡単に聞いてくださいxterm位置をランダムに大きい行番号に設定します。たとえば、次のようになります。

tput cup 9999 0

(ウィンドウの行が10,000個未満であると仮定する場合は無視してください。ロールバック)。

ウィンドウのサイズ変更時に文字列は変更されないため、一度計算してからプロンプト文字列に定数として貼り付けることができます。例:

TPUT_END=$(tput cup 9999 0)

それから

PS1="${TPUT_END} myprompt: "

あなたの好みに応じて。

他のプロセス修正の場合は、必要に応じて表示されることを確認するには、これらの変更後に再計算する必要がありますPS1PS1しかし、質問に指摘するのに十分な詳細はありません。どこ変える。

最後に、bashの仮定のために、タブ補完の動作はこのタイプの変更と一致しません。

関連情報