Fixing the present working directory in tmux sessions to refer to the logical rather than physical working directory

Jeff Wintersinger
by Jeff Wintersinger
October 02, 2020

The problem

When using the tmux terminal multiplexer, it will open new windows with the present working directory $PWD set to whatever it was when you created the tmux session. However, if $PWD includes a symlink referring to some other physical path, tmux will know only about the physical path, and so $PWD in new tmux windows will refer to the physical rather than logical path. This is not a tmux bug—conceptually, there is seemingly no way for tmux to achieve the desired behaviour, since it only ever knows about the physical directory. For instance, my $HOME is set to /home/q/qmorris/jawinter, which resolves to /gpfs/fs1/home/q/qmorris/jawinter, since /home is symlinked to /gpfs/fs1/home. Unfortunately, if you launch tmux in a subdirectory relative to $HOME, it can see only the physical path. This results in a horrifically ugly $PS1 prompt in Bash, since tmux will instruct Bash to use as the working directory whatever path tmux has resolved.

Oww, my eyes.

Our goal is to have Bash use the logical working directory, which allows it to strip the leading portion of the directory that corresponds to the home directory when generating the $PS1 prompt.

Ahhh, this is much nicer.

The solution

While there exist a number of possible solutions that force tmux to use the logical working directory, all seemed overly complex or fragile. I came up with a different method that requires only dropping the following snippet in your ~/.bashrc.

function correct_home {
    [[ -n "$TMUX" ]] || return
    _BASEHOME=$(realpath $HOME)
    [[ $PWD =~ ^$_BASEHOME ]] || return
    cd $(echo $PWD | sed 's|^'$_BASEHOME'|'$HOME'|')
}
correct_home

This function will be executed only once when the shell is launched by a new window created through tmux. As tmux sets the $TMUX environment variable, we can detect when this code is run outside tmux and abort immediately. If execution proceeds, the code simply detects if the shell’s $PWD is under the physical home directory, and replaces this portion with the logical path that Bash knows about through $HOME.


Post navigation