Bash TTY – Resolving Issues with Writing Two Shell Prompts

bashstracetty

I am looking at the strace output of a running bash process connected to
a terminal, for educational purposes.

My bash process has PID 2883.

I type

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

Into a terminal. I then go into my bash process, and have the following
interaction:

[OP@localhost ~]$ ls

Looking at the output, I see

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

I am confused at the last two lines. It appears that bash is attempting
to write two shell prompts? What's going on here?

Best Answer

The <ESC>]0; sequence (shown as \33]0; by strace) is the escape sequence to set the terminal window title. It's terminated with the BEL character (\7), so the first write sets the window title. The second prints the actual prompt. Note that even apart from the escape sequence, they're not exactly the same. The prompt has a surrounding [..] while the window title doesn't.

We can also see that the first write goes to stdout (fd 1, the first argument to write()), and the second to stderr. Bash prints the prompt to stderr, so the first write comes from somewhere else. That somewhere is probably PROMPT_COMMAND, like the one in Debian's default startup scripts for Bash. There's something like this in there:

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

It sets that PROMPT_COMMAND if running xterm or rxvt, which should support that escape sequence.

Related Question