Recently I needed to use tmux. Turned out that it wasn't even installed on my
system, which, of course, wasn't a problem. After it was installed and ran I
was presented with a prompt that didn't responed to anything. The only thing
that worked well was pkill tmux
.
Four hours of tries showed that tmux seems to work in general, but it waits
eternally for something inside select()
system call. The strangest part of
all this was that it worked just fine in native linux console and through SSH.
As a workaround just to be able to use it I did this ugly thing:
- Started
sshd
. - Connected to localhost from
xterm
. - Ran and used tmux.
Then I noticed another strange thing, bash prompt was of regular grey color.
Trying to use other of my customizations (aliases, functions etc.) didn't
succeed either. I found it! My .bashrc
broke tmux. Bisection through
commenting parts of the .bashrc
revealed this piece of code:
# check cursor position and add new line if we're not in the first column
function prompt-command()
{
exec < /dev/tty
local OLDSTTY=$(stty -g)
stty raw -echo min 0
echo -en "\033[6n" > /dev/tty && read -sdR CURPOS
stty $OLDSTTY
[[ ${CURPOS##*;} -gt 1 ]] && echo "${color_error}↵${color_error_off}"
}
PROMPT_COMMAND='prompt-command'
Do you see what is wrong with it? (By the way, it's based on this StackOverflow answer).
No? Here it is:
exec < /dev/tty
The stdin
of the shell is changed, but never restored. And tmux seems to be
unable to handle such redirection (GNU screen
works well with it). Now when
the reason is known, I can reproduce the issue on demand with this command:
exec < /dev/tty && tmux
I see two possible ways to solve the issue:
- Run body of the function in another process (sub-shell fits well in this case).
- Save and restore old value of
stdin
.
The first solution could look like this:
# check cursor position and add new line if we're not in the first column
function prompt-command()
{
(
exec < /dev/tty
local OLDSTTY=$(stty -g)
stty raw -echo min 0
echo -en "\033[6n" > /dev/tty && read -sdR CURPOS
stty $OLDSTTY
[[ ${CURPOS##*;} -gt 1 ]] && echo "${color_error}↵${color_error_off}"
)
}
PROMPT_COMMAND='prompt-command'
And the second one:
# check cursor position and add new line if we're not in the first column
function prompt-command()
{
exec 100<&0
exec < /dev/tty
local OLDSTTY=$(stty -g)
stty raw -echo min 0
echo -en "\033[6n" > /dev/tty && read -sdR CURPOS
stty $OLDSTTY
[[ ${CURPOS##*;} -gt 1 ]] && echo "${color_error}↵${color_error_off}"
# restore real stdout and close duplicate
exec 0<&100 100<&-
}
PROMPT_COMMAND='prompt-command'
I chose to use the second variant even though it requires assumption that file descriptor number 100 is unused. This should generally be faster than forking a process.