Bash尝试编写两个shell提示?


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
...

我对最后两行感到困惑。看来bash正在尝试编写两个shell提示?这里发生了什么?

Answers:


24

<ESC>]0;序列(如\33]0;strace 所示)是用于设置终端窗口标题的转义序列。它以BEL字符(\7)终止,因此第一个write设置窗口标题。第二个打印实际的提示。请注意,除了转义序列外,它们也不完全相同。提示周围有一个提示,[..]而窗口标题则没有。

我们还可以看到,第一次写入stdout(fd 1,第一个参数为write()),第二次写入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似乎是逐个字符地读取事物,而不是一次一行地读取吗?另外,为什么bash会在stdout中写入“ l”和“ s”?如果我使用进行类似的跟踪cat,则有两个区别:它逐行读取输入,并且确实将输入回显到stdout,但我却看到了两次输入(输入时一次,当cat回显一次)。
极端的斧头

@ extremeaxe5,这基本上是因为Bash(或更确切地说是readline库)本身处理所有命令行处理,而不是依赖于终端完成的相当有限的处理。它必须立即获得输入,以决定在^A按下TAB字符或(Ctrl-A)或各种特殊字符时该怎么做。另外,它关闭终端的回声,以便它可以决定为每个特定输入字符输出什么(再次,TAB通常不输出TAB。)cat不执行任何操作。如果是这样,请尝试运行dash,它不会执行任何命令行处理。
ilkkachu

实际上,Bash调用一次read()只能读取一个字节的原因是它无法读取换行符。换行符可能导致它运行一个外部程序,该程序也可能从同一输入中读取。(并且该程序应该能够在换行符之后读取任何字符。)如果不必担心,它可以read()以更大的限制进行调用,并且终端处于原始模式时,它通常仍会获取输入一次一个字符。(这取决于输入字符到达的速度以及进程的调度方式。)
ilkkachu

您的第二条评论似乎是正确的,因为Bash会自行进行命令行处理。
Extremeaxe5

@ extremeaxe5,嗯,是的,我以为是这样,因为无论如何这是常见的情况。但是,即使外壳依靠终端的线路编辑,时间安排仍然可能是个问题。如果快速连续发送了两行(请考虑粘贴数据),并且系统已加载得足够多,因此无法立即安排外壳程序(或更糟糕的是,外壳程序已停止),则read()缓冲区较大的缓冲区可能仍会返回两行相同的电话。我不认为有一个保证,read()始终在熟模式返回只有一行。
ilkkachu
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.