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で画面を消去すると印刷されず、エイリアスも役に立ちませ
clear
んclear; tput ...
。 - 端末のサイズ変更時に別の場所に移動するように求めるメッセージを表示
答え3
私はしばらくの間\[\033[1000H\]
ソリューションに@ Thomas Dickeyのインクルードを使用してきました。PS1
過去数ヶ月の間に問題を数回調査した後、ついに問題を防ぐ小さな変更が見つかりました。 ANSIエスケープシーケンスはに含まれてはいけませんが、from(inまたは他の設定ファイル)PS1
に直接印刷する必要があります。stdout
PROMPT_COMMAND
~/.bashrc
PROMPT_COMMAND=prompt
prompt() {
r="${?}"
printf '\033[1000H'
return "${r}"
}
printf
そしてreturn
組み込み関数(参考資料を参照builtins(1)
)なので、プロセスは作成されません。前のコマンドの終了ステータスはに保存されr
、Clearを使用してprintf
Restoreを使用します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_COMMAND
。clear-screen
一方、readlineコマンドを使用すると、readline(Bashで使用)が画面消去要求を傍受して再実行せずに、古いプロンプトを簡単に再表示できますPROMPT_COMMAND
。\033[1000H
エスケープシーケンスはもはやプロンプト自体にはありませんが、runningの副作用なので、プロンプトPROMPT_COMMAND
は元の場所に残ります。
最も一般的な解決策は、PROMPT_COMMAND
ターミナルのサイズ変更clear-screen
または実行時に実行されるようにBashをパッチすることです。しかし、私はより簡単な解決策を選びました。つまり、直接印刷するようにコマンドをパッチすることですclear-screen
。明確に言えば、Bashのデフォルトソース構成には独自のコピーが含まれていますが、私のシステム(Gentoo Linux)では、デフォルトのBash構成はシステムを使用するものであり、パッチシステムはそれを使用する他のプログラムでも機能します。\033[1000H
stdout
readline
readline
readline
とにかく最も簡単な解決策は、Bashをダウンロードし、patch -p1
Bashソースディレクトリに次のパッチを適用することです。
--- 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: "
あなたの好みに応じて。
他のプロセス修正の場合は、必要に応じて表示されることを確認するには、これらの変更後に再計算する必要がありますPS1
。PS1
しかし、質問に指摘するのに十分な詳細はありません。どこ変える。
最後に、bashの仮定のために、タブ補完の動作はこのタイプの変更と一致しません。