Show the Current Directory in the Terminal Title with FreeBSD sh
When several terminals are open at the same time, it is useful to identify their working directories from their title bars.
For example, if the current directory is:
/home/hodong/projects/nimfsoft.com
the terminal title can display:
~/projects/nimfsoft.com
If the current directory is outside the home directory, the full path is displayed instead:
/usr/local/src
This article uses FreeBSD /bin/sh and ~/.shrc.
How the terminal title is changed
xterm-compatible terminal emulators can change their title using an OSC, or Operating System Command, escape sequence.
printf '\033]0;%s\007' 'New terminal title'
The sequence consists of:
\033 ESC
]0; Set the icon name and window title
%s Text to display
\007 Terminate the OSC sequence with BEL
The current directory can therefore be used directly:
printf '\033]0;%s\007' "$PWD"
Support depends on the terminal emulator and its configuration.
Abbreviating the home directory as ~
When $PWD is equal to $HOME, or is below it, the home directory prefix can
be replaced with ~.
set_terminal_title() {
if [ -n "${HOME-}" ]; then
case "$PWD" in
"$HOME")
_terminal_title_path='~'
;;
"$HOME"/*)
_terminal_title_path="~${PWD#"$HOME"}"
;;
*)
_terminal_title_path=$PWD
;;
esac
else
_terminal_title_path=$PWD
fi
printf '\033]0;%s\007' "$_terminal_title_path"
unset _terminal_title_path
}
${HOME-} expands to an empty string when HOME is unset. This prevents an
unset variable from being treated as a valid home-directory prefix.
When the home directory is set, the case statement classifies the current
path:
case "$PWD" in
"$HOME")
_terminal_title_path='~'
;;
"$HOME"/*)
_terminal_title_path="~${PWD#"$HOME"}"
;;
*)
_terminal_title_path=$PWD
;;
esac
If the current directory is exactly the home directory, the title becomes
~.
/home/hodong -> ~
If the current directory is below the home directory, the $HOME prefix is
removed and ~ is placed in front of the remaining path.
/home/hodong/projects/nimfsoft.com
-> ~/projects/nimfsoft.com
${PWD#"$HOME"} removes the shortest prefix matching $HOME from $PWD.
For example, given:
HOME=/home/hodong
PWD=/home/hodong/projects/nimfsoft.com
produces:
/projects/nimfsoft.com
Adding ~ in front produces the final title:
~/projects/nimfsoft.com
The separate "$HOME"/* pattern prevents a path such as
/home/hodong2/projects from being incorrectly considered part of
/home/hodong.
/home/hodong2/projects
-> /home/hodong2/projects
_terminal_title_path is a temporary shell variable. Shell function variables
remain in the current shell by default, so it is removed with unset after
use to reduce the chance of a name collision.
Updating the title after cd
Setting the title only once when the shell starts is not enough. It also needs to be updated whenever the working directory changes.
The following function calls the original shell builtin cd, then updates the
title only when the directory change succeeds:
cd() {
command cd "$@" || return $?
set_terminal_title
}
Calling cd "$@" directly from inside the function would call the same
function again and cause recursion.
cd "$@"
Using command cd "$@" bypasses the function with the same name and invokes
the shell builtin cd.
command cd "$@"
"$@" preserves all arguments:
cd
cd -
cd /usr/local/src
If cd fails, its status is returned and the title is left unchanged.
command cd "$@" || return $?
Therefore, trying to move to a nonexistent directory leaves both the current working directory and the terminal title unchanged.
Final .shrc patch
Apply the following patch to ~/.shrc:
--- a/.shrc 2026-07-15 19:28:53.358286000 +0900
+++ b/.shrc 2026-07-15 19:47:56.461994000 +0900
@@ -46,6 +46,37 @@
bind ^[[5~ ed-move-to-beg
bind ^[[6~ ed-move-to-end
+# Update terminal title to the current working directory.
+set_terminal_title() {
+ if [ -n "${HOME-}" ]; then
+ case "$PWD" in
+ "$HOME")
+ _terminal_title_path='~'
+ ;;
+ "$HOME"/*)
+ _terminal_title_path="~${PWD#"$HOME"}"
+ ;;
+ *)
+ _terminal_title_path=$PWD
+ ;;
+ esac
+ else
+ _terminal_title_path=$PWD
+ fi
+
+ printf '\033]0;%s\007' "$_terminal_title_path"
+ unset _terminal_title_path
+}
+
+# Change directory and update the terminal title.
+cd() {
+ command cd "$@" || return $?
+ set_terminal_title
+}
+
+# Set the initial terminal title.
+set_terminal_title
+
# set prompt: ``username@hostname:directory $ ''
PS1="\u@\h:\w \\$ "
The initial call to set_terminal_title reflects the current directory at the
time .shrc is loaded.
set_terminal_title
After that, the cd wrapper updates the title whenever a directory change
succeeds.
Reload the configuration:
. ~/.shrc
Moving into the project directory:
cd ~/projects/nimfsoft.com
sets the title to:
~/projects/nimfsoft.com
Moving outside the home directory displays the full path:
cd /usr/local/src
/usr/local/src
Why the function is not called from PS1
One might try:
PS1='$(set_terminal_title)\u@\h:\w \$ '
In the FreeBSD sh environment used for this article, that form did not work
as intended. The prompt was displayed incorrectly and the terminal title was
not updated.
The existing prompt is therefore kept unchanged:
PS1="\u@\h:\w \\$ "
Instead of depending on prompt re-evaluation behavior, the configuration
updates the title immediately after a successful cd.
Notes
This configuration requires a terminal emulator that supports OSC title sequences and permits applications to change the title.
It assumes ordinary directory names. If a directory name can contain terminal control characters such as ESC or BEL, the path should be sanitized before it is inserted into an OSC sequence.
The title is updated when the directory is changed through the cd function.
The explicit initial call handles the shell’s starting directory.
Summary
The resulting title rules are:
/home/hodong -> ~
/home/hodong/projects -> ~/projects
/home/hodong/projects/nimfsoft.com -> ~/projects/nimfsoft.com
/home/hodong2/projects -> /home/hodong2/projects
/usr/local/src -> /usr/local/src
This small configuration makes it easier to distinguish terminals when working on several projects at once.