Moving back to Bash

Back in macOS Catalina, Apple changed the default shell from bash to zsh. For years I continued to just use zsh as I saw no real difference, however recently I've decided to move back to bash after watching a lot of Dave Eddy's videos. I think I sold it to myself as a good idea, mainly because my servers use bash by default, and most of my script have the shebang #!/usr/bin/env bash not #!/usr/bin/env zsh. That's not to say that I did not try to dabble with zsh. But coming from Linux, I's always end up falling back to bash, so why not switch my shell.

Bash versions

The default bash version shipped with macOS is super old.

/bin/bash --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin25)
Copyright (C) 2007 Free Software Foundation, Inc.

Whereas, the bash from brew is much newer.

/opt/homebrew/bin/bash --version
GNU bash, version 5.3.3(1)-release (aarch64-apple-darwin25.0.0)
Copyright (C) 2025 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

This means, when using chsh to change the shell, I want to use the one from Homebrew, and not the one shipped with macOS.

Setup

  1. add /opt/homebrew/bin/bash to /etc/shells
  2. run chsh -s /opt/homebrew/bin/bash
  3. add eval "$(/opt/homebrew/bin/brew shellenv)" in ~/.bash_profile
  4. add this to ~/.bash_profile too
if [ -f ~/.bashrc ]; then
    source ~/.bashrc
fi

The only thing now, is to setup your ~/.bashrc the way you want it. and then confirm its all as expected.

echo $SHELL && echo $BASH_VERSION
/opt/homebrew/bin/bash
5.3.3(1)-release

My prompt

The look and feel of my PS1 is really important to me, in zsh I had it the way I liked. After a Sunday afternoon, my bash prompt code now looks like this, which emulates what I had in zsh.

if [ -f "${HOME}/.abbr_pwd.bash" ]; then
    source "${HOME}/.abbr_pwd.bash"

    FG_RED='\[\e[31m\]'
    FG_GREEN='\[\e[32m\]'
    FG_WHITE='\[\e[37m\]'
    FG_RESET='\[\e[0m\]'

    git_info() {
        local dir="$PWD"
        while [ "$dir" != "/" ]; do
            if [ -d "$dir/.git" ]; then
                if branch=$(git -C "$dir" symbolic-ref -q --short HEAD 2>/dev/null); then
                    echo "$branch"
                elif tag=$(git -C "$dir" describe --tags --exact-match --abbrev=0 HEAD 2>/dev/null); then
                    echo "$tag"
                else
                    git -C "$dir" rev-parse --short HEAD
                fi
                return
            fi
            dir="$(dirname "$dir")"
        done
    }

    set_prompt() {
        local last_exit=$?
        local exit_status_display=""
        if [ $last_exit -ne 0 ]; then
            exit_status_display="${FG_RED}(${last_exit})${FG_RESET}"
        fi

        local abbr_path="$(pwd_abbr)"
        local venv=""
        local venv_display=""
        if [ -n "$VIRTUAL_ENV" ]; then
            venv="$(basename "$VIRTUAL_ENV")"
            venv_display="${FG_GREEN}${venv}${FG_RESET}"
            export PATH="$VIRTUAL_ENV/bin:$PATH"
        fi

        local git=""
        local git_display=""
        git="$(git_info)"
        if [ -n "$git" ]; then
            git_display="${FG_GREEN}${git}${FG_RESET}"
        fi

        local middle=""
        if [ -n "$venv_display" ]; then
            middle+=" ${venv_display}"
        fi
        if [ -n "$git_display" ]; then
            middle+=" ${git_display}"
        fi

        export PS1="${FG_WHITE}[${abbr_path}${middle}${FG_WHITE}]${exit_status_display}\\$ ${FG_RESET}"
    }
    PROMPT_COMMAND="set_prompt"

else
    export PS1='\u@\h:\w\$ '
fi

Where .abbr_pwd.bash looks like this;

pwd_abbr() {
    local path="${PWD}"
    local home="${HOME}"

    if [[ "$path" == "$home"* ]]; then
        path="~${path#$home}"
    fi

    IFS='/' read -r -a parts <<< "$path"

    local result=""
    local i
    local len=${#parts[@]}

    for (( i=0; i<$len; i++ )); do
        local part="${parts[i]}"
        if [[ "$part" == "~" ]]; then
            result="~"
        elif (( i == len - 1 )); then
            result+="/$part"
        elif [[ -n "$part" ]]; then
            result+="/${part:0:1}"
        fi
    done

    echo "$result"
}

Now I get a nice looking prompt :)

bash-prompt.png