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.