Custom $PROMPT_COMMAND
Did you know you can customise the user@host:/path $
prompt which appears in your terminal? It's called the
PS1, I've known it can be changed for ages but I'd never seen any examples which
added any real utility, they were always overblown and kinda silly, and I'd mentally marked it as not-useful and ignored
it ever since. I also spent quite a lot of my time switching between servers where the prompts had to stay as the
defaults.
Recently I was looking at the prompt thinking that there is quite a lot in there that never changes. For example on my single user dev machine I don't need to see my username or hostname, they never change and just take up space.
A couple of hours of hacking later I'd settled on a prompt which was just /path/to/cwd/ $
, this is considerably
shorter than the default joeblogs@pc-1234-abcd:/path/to/cwd/ $
.
I also sprinkled in a little Git info when the current working directory is a repo, simply the current branch name followed by a * or ➚ if there are uncommitted/unpushed changes.
If you want something similar dump this in your ~/.bashrc
file:
# Git prompt components GIT_PROMPT_PREFIX=" \[\e[1;35m\]" GIT_PROMPT_SUFFIX="\[\e[0m\]" GIT_PROMPT_DIRTY="*" GIT_PROMPT_UNPUSHED="➚" GIT_PROMPT_CLEAN="" # Flag to track if this is the first prompt FIRST_PROMPT=true function parse_git_branch() { git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' } function parse_git_status() { local status="" # Check for dirty/untracked files local git_status="$(git status -s 2> /dev/null)" if [[ -n "$git_status" ]]; then status+="$GIT_PROMPT_DIRTY" fi # Check for unpushed commits local branch=$(parse_git_branch) if [[ -n "$branch" ]]; then local remote=$(git for-each-ref --format='%(upstream:short)' "refs/heads/$branch" 2> /dev/null) if [[ -n "$remote" ]]; then local unpushed=$(git rev-list "$remote..HEAD" 2> /dev/null | wc -l) if [[ $unpushed -gt 0 ]]; then status+="$GIT_PROMPT_UNPUSHED" fi else # If no upstream is set, mark as unpushed status+="$GIT_PROMPT_UNPUSHED" fi fi echo -n "$status" } function git_prompt_info() { local branch=$(parse_git_branch) if [[ -n "$branch" ]]; then echo -n "$GIT_PROMPT_PREFIX$branch$(parse_git_status)$GIT_PROMPT_SUFFIX" fi } # Set the prompt function set_bash_prompt() { # Need to capture the output of the functions for PS1 local git_info="$(git_prompt_info)" # Set the terminal title to the current working directory echo -ne "\033]0;${PWD}\007" # Conditional newline based on whether this is the first prompt local newline="" if [[ "$FIRST_PROMPT" = false ]]; then newline='\n' else FIRST_PROMPT=false fi # Prompt with conditional newline PS1="$newline"'\[\e[1;32m\]\w/\[\e[0m\]'"$git_info"' $ ' } PROMPT_COMMAND=set_bash_prompt
Update (27 Sept 2025) - Now With More Kubernetes
I found it useful as a sanity check to know which Kubernetes cluster my current CLI is targetting. It will only show
once a kubectl
command is run so it isn't constantly cluttering up the CLI. This isn't ideal as you'd want to know
before you run the command but in the vast majority of cases I will run a read-only command first like kubectl get nodes
# Git prompt components GIT_PROMPT_PREFIX=" \[\e[1;35m\]" GIT_PROMPT_SUFFIX="\[\e[0m\]" GIT_PROMPT_DIRTY="*" GIT_PROMPT_UNPUSHED="➚" GIT_PROMPT_CLEAN="" # Kubernetes prompt components K8S_PROMPT_PREFIX=" \[\e[1;36m\][" K8S_PROMPT_SUFFIX="]\[\e[0m\]" # Flag to track if this is the first prompt FIRST_PROMPT=true # Variable to track if kubectl has been used KUBECTL_USED=false function parse_git_branch() { git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/' } function parse_git_status() { local status="" # Check for dirty/untracked files local git_status="$(git status -s 2> /dev/null)" if [[ -n "$git_status" ]]; then status+="$GIT_PROMPT_DIRTY" fi # Check for unpushed commits local branch=$(parse_git_branch) if [[ -n "$branch" ]]; then local remote=$(git for-each-ref --format='%(upstream:short)' "refs/heads/$branch" 2> /dev/null) if [[ -n "$remote" ]]; then local unpushed=$(git rev-list "$remote..HEAD" 2> /dev/null | wc -l) if [[ $unpushed -gt 0 ]]; then status+="$GIT_PROMPT_UNPUSHED" fi else # If no upstream is set, mark as unpushed status+="$GIT_PROMPT_UNPUSHED" fi fi echo -n "$status" } function git_prompt_info() { local branch=$(parse_git_branch) if [[ -n "$branch" ]]; then echo -n "$GIT_PROMPT_PREFIX$branch$(parse_git_status)$GIT_PROMPT_SUFFIX" fi } function get_k8s_context() { if [[ "$KUBECTL_USED" = true ]]; then local context=$(kubectl config current-context 2>/dev/null) if [[ -n "$context" ]]; then echo -n "$K8S_PROMPT_PREFIX$context$K8S_PROMPT_SUFFIX" fi fi } # Function to track kubectl usage function kubectl() { KUBECTL_USED=true command kubectl "$@" } # Set the prompt function set_bash_prompt() { # Need to capture the output of the functions for PS1 local git_info="$(git_prompt_info)" local k8s_info="$(get_k8s_context)" # Set the terminal title to the current working directory echo -ne "\033]0;${PWD}\007" # Conditional newline based on whether this is the first prompt local newline="" if [[ "$FIRST_PROMPT" = false ]]; then newline='\n' else FIRST_PROMPT=false fi # Prompt with conditional newline PS1="$newline"'\[\e[1;32m\]\w/\[\e[0m\]'"$git_info$k8s_info"' $ ' } PROMPT_COMMAND=set_bash_prompt