Bashは2つのシェルプロンプトを書き込もうとしますか?


11

教育目的で、ターミナルに接続された実行中のbashプロセスのstrace出力を調べています。

私のbashプロセスにはPID 2883があります。

私はタイプする

[OP@localhost ~]$ strace -e trace=openat,read,write,fork,vfork,clone,execve -p 2883 2> bash.strace

ターミナルへ。次に、bashプロセスに進み、次の対話を行います。

[OP@localhost ~]$ ls

出力を見ると、

strace: Process 2883 attached
read(0, "l", 1)                         = 1
write(2, "l", 1)                        = 1
read(0, "s", 1)                         = 1
write(2, "s", 1)                        = 1
read(0, "\r", 1)                        = 1
write(2, "\n", 1)                       = 1
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fec6b1d8e50) = 3917
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3917, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
write(1, "\33]0;OP@localhost:~\7", 23) = 23
write(2, "[OP@localhost ~]$ ", 22)  = 22
...

最後の2行で混乱しています。bashは2つのシェルプロンプトを作成しようとしているようです。何が起きてる?

回答:


24

<ESC>]0;(として示される配列\33]0;straceのによる)は、端末ウィンドウのタイトルを設定するエスケープシーケンスです。BEL文字(\7)で終了しているため、最初の文字writeはウィンドウタイトルを設定します。2番目は実際のプロンプトを出力します。エスケープシーケンスを除けば、まったく同じではないことに注意してください。プロンプトには周囲が[..]ありますが、ウィンドウタイトルにはありません。

また、最初の書き込みはstdout(fd 1、への最初の引数write())に行き、2番目の書き込みはstderrに行きます。Bashはプロンプトをstderrに出力するため、最初の書き込みは別の場所から行われます。それはおそらくPROMPT_COMMAND、DebianのBashのデフォルトの起動スクリプトにあるようなものです。そこには次のようなものがあります:

case "$TERM" in
xterm*|rxvt*)
    PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
    ;;
*)
    ;;
esac

その設定PROMPT_COMMAND実行した場合xtermまたはrxvtそのエスケープシーケンスをサポートするべきです。


bashが一度に1行ずつ読み取るのではなく、文字ごとに物事を読み取るように見える理由を知っていますか?また、bashが「l」と「s」をstdoutに書き込むのはなぜですか?同様のstraceをで実行するとcat、2つの違いがあります。1行ずつ入力を読み取り、入力をstdoutにエコーしますが、入力を2回表示します(1回入力すると1回、catがエコーすると1回)。
Extremeaxe5

@ extremeaxe5、それは基本的にBash(またはむしろreadlineライブラリ)が端末によって行われるかなり限定された処理に依存するのではなく、すべてのコマンドライン処理自体を処理するためです。たとえば、TAB文字または^A(Ctrl-A)またはさまざまな特殊文字が押されたときに何をするかを決定するには、すぐに入力を取得する必要があります。また、端末のエコーをオフにして、特定の入力文字ごとに何を出力するかを決定できるようにします(ここでも、TABは通常TABを出力しません)cat。もしそうならdash、を実行してみてください。コマンドライン処理は何も行われません。
ilkkachu

実際、Bashがread()一度に1バイトのみを読み取るように呼び出す理由は、改行を超えて読み取ることができないためです。改行によって外部プログラムが実行され、同じ入力から読み取られる場合があります。(そして、そのプログラムは改行の後のすべての文字を読み取ることができるはずです。)それを気にする必要がなかった場合read()、より大きな制限で呼び出し、端末をrawモードにしても、通常は入力を取得します。一度に1文字。(入力文字の到着速度とプロセスのスケジュール
次第

Bash自体がコマンドライン処理を行うため、2番目のコメントは真実であるように見えます。
Extremeaxe5

@ extremeaxe5、まあ、はい、それはとにかく一般的なケースなので、私はそれを想定していました。ただし、シェルが端末の行編集に依存していたとしても、タイミングが問題になる可能性があります。2つの行が連続して送信され(データの貼り付けを考える)、システムが十分にロードされてシェルがすぐにスケジュールされない(または、さらに悪いことに、シェルが停止する)場合、read()大きなバッファーを使用しても、両方の行が返されます。同じ呼び出し。私は保証はありだとは思わないread()だろう、常に cookedモードで一つだけの行を返すが。
ilkkachu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.