Fixing Terminal.app window titles where directory names contain extended characters…

This one may be a little esoteric, but I’ve always configured my ~/.bashrc to set $PS1 in bash to a simple ‘$ ‘, but show all relevant information in the terminal (or screen) window title.

This is generally fine but, on OS X at least, if a directory name contains an extended character such as ‘Æ‘ then what actually happens is that all characters are sent to the window title except for the extended character, which is pre-pended to the prompt for the following line. Being a double-byte character, this then confused the shell’s line-editing abilities, and characters have a tendency to disappear if the cursor keys are used for editing.

After suffering with this for several months, this interesting problem reached my needs-a-fix threshold and I decided to do something about it 😉

The default setting in .bashrc (originally courtesy of Gentoo, I believe) is:

# Change the window title of X terminals
case $TERM in
        xterm*|rxvt|Eterm|eterm)
                PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME%%.*}:${PWD/$HOME/~}\007"'
                PS1='$ '
                ;;
        screen)
                PROMPT_COMMAND='echo -ne "\033_${USER}@${HOSTNAME%%.*}:${PWD/$HOME/~}\033\"'
                PS1='$ '
                ;;
        *)
                PROMPT_COMMAND='echo -ne "${USER}@${HOSTNAME%%.*}:${PWD/$HOME/~} \$ "'
                ;;
esac

… which, in the first two instances, embeds the necessary control codes to send output to the title rather than the console. PROMPT_COMMAND is executed on every time the shell writes a prompt, so it can’t be too heavy-weight to evaluate. Also note that neither ‘$( ... )‘ nor ‘`...`‘ syntax can be used here, as these expressions would be evaluated to a fixed value when .bashrc is read.

My solution is:

case $TERM in
        xterm*|rxvt|Eterm|eterm)
                PROMPT_COMMAND='pwd | xargs echo -n | sed "s|[^[:punct:]a-zA-Z ]|_|g ; s|$HOME|~|" | xargs -0 printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}"'
                PS1='$ '
                ;;

It has to be admitted that most of the above is to work around various quirks. For example, the only to convince ‘sed‘ not to append a newline (which would be sent to the window itself) to its output is to use a recent GNU version and to strip newlines from the source data – which is what the first invocation of ‘xargs‘ allows. We then attempt to strip out any extended characters – noting that all of sed‘s alpha-numeric character classes include the characters that we’re trying to remove. Finally, the filtered result is passed (spaces and all, due to the ‘-0‘ option – so that everything falls within the single ‘%s‘ format-specifier) to printf – using ‘echo‘ or trying to get away without a second invocation of ‘xargs‘ would, so far as I can tell, always hit the aforementioned fixing-on-evaluation issue.

Although more complex, there doesn’t seem to be appreciable lag on modern machines.

Another little niggle bites the dust 🙂