分享Bash Prompt设置

我使用了两个轻量级的 Bash 函数来构建一个高性能、信息丰富但极简的命令行提示符:

1. Git 状态提示: __fast_git_ps1

这是一个极简但高效的 Git prompt 实现:

  • 只调用一次 Git(使用 --porcelain=v2
  • 显示当前分支、已暂存 +、未暂存 *
  • 自动处理 detached HEAD
  • 不依赖外部工具(无 sed/awk/grep)
  • 性能极快,适合大仓库

它会根据传入的 prefix/suffix 自动构建完整的 PS1。

2. Python / Node 环境提示: __fast_env_ps1

这个函数用于显示当前的开发环境版本:

  • 检测 pyenv 的 Python 虚拟环境(显示为 P3.12
  • 检测 nvm 的 Node 版本(显示为 N22.22
  • 无虚拟环境时不显示任何内容
  • 不调用外部命令,只读取环境变量
  • 性能开销几乎为零

3. 最终 Prompt 整合

我将两者整合进 PROMPT_COMMAND,并加入颜色区分:

  • 用户名、主机名、路径使用不同颜色
  • Git 信息使用紫色
  • Python/Node 信息统一使用青色
  • 最终形成一个双行、清晰、极简、无延迟的提示符

示例效果:

~/my-bash-setup/env-prompt.sh,

#!/usr/bin/env bash
# Ultra-simple environment prompt for Bash: python-version node-version
# Usage:
#   export PROMPT_COMMAND='history -a;__fast_env_ps1 "<prefix>" "<suffix>" [format]'

__fast_env_ps1() {
    local py="" node=""
    # ----- Python (pyenv) -----
    if [[ -n "$PYENV_VERSION" ]]; then
        if [[ "$PYENV_VERSION" =~ ([0-9]+)\.([0-9]+) ]]; then
            py="P${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
        fi
    fi

    # ----- Node (nvm) -----
    if [[ -n "$NVM_BIN" ]]; then
        if [[ "$NVM_BIN" =~ /v([0-9]+)\.([0-9]+) ]]; then
            node="N${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
        fi
    fi

    printf "%s%s" "${py:+ $py}" "${node:+ $node}"
}

~/my-bash-setup/git-branch-prompt.sh,

#!/usr/bin/env bash
# Ultra-simple Git prompt for Bash: branch + staged (+) + unstaged (*)
# Always recomputes on each prompt draw (still only 1 Git call).
# Usage:
#   export PROMPT_COMMAND='history -a;__fast_git_ps1 "<prefix>" "<suffix>" [format]'

__fast_git_ps1() {
  local prev_exit="$?"
  local pcmode=no ps1_start="" ps1_end="" printf_format=' (%s)'

  case "$#" in
    2|3)
      pcmode=yes
      ps1_start="$1"
      ps1_end="$2"
      printf_format="${3:-$printf_format}"
      PS1="$ps1_start$ps1_end"
      ;;
    0|1)
      printf_format="${1:-$printf_format}"
      ;;
    *)
      return "$prev_exit"
      ;;
  esac

  # Compute segment: branch + staged (+) + unstaged (*)
  local status
  status="$(git -c status.aheadBehind=false status \
              --porcelain=v2 --branch \
              --untracked-files=no \
              --ignore-submodules=all 2>/dev/null)" || {
    [[ "$pcmode" == yes ]] && return "$prev_exit"
    return "$prev_exit"
  }

  local branch="" staged="" unstaged=""
  while IFS= read -r line; do
    case "$line" in
      \#\ branch.head*)
        branch="${line##* }"
        ;;
      [12u]\ *)
        local x="${line:2:1}" y="${line:3:1}"
        [[ "$x" != "." ]] && staged="+"
        [[ "$y" != "." ]] && unstaged="*"
        [[ -n "$staged" && -n "$unstaged" ]] && break
        ;;
    esac
  done <<<"$status"

  if [[ "$branch" == "(detached)" ]]; then
    branch="$(git rev-parse --short HEAD 2>/dev/null)"
  fi

  local out
  printf -v out -- "$printf_format" "${branch}${staged}${unstaged}"

  if [[ "$pcmode" == yes ]]; then
    PS1="$ps1_start$out$ps1_end"
  else
    printf '%s' "$out"
  fi

  return "$prev_exit"
}

在~/.bashrc中使用,

export MAGENTA="\033[1;31m"
export ORANGE="\033[1;33m"
export GREEN="\033[1;32m"
export PURPLE="\033[1;35m"
export WHITE="\033[1;37m"
export CYAN="\033[1;36m"
export RESET="\033[m"

source "$HOME/my-bash-setup/git-branch-prompt.sh"
source "$HOME/my-bash-setup/env-prompt.sh"
# Refresh every 1s (default); force refresh on cd (default)
export PROMPT_COMMAND='history -a; __fast_git_ps1 "${MAGENTA}\u${RESET}@${ORANGE}\h${RESET}:${GREEN}\w${RESET}${PURPLE}" "${CYAN}$(__fast_env_ps1)${RESET}\n\$ "'
export PS2="${ORANGE}->${RESET}"
8 个赞

git status用porcelain v1的话性能会不会好一点?

很好,从头定制,非常符合 emacs 等的精神

不太清楚,AI写的代码.我只是给AI提了需求加小修小补.

git自带一个prompt bash 设置,那个实在太慢了.不过我让AI先理解那个设置再写新版本.

我之前做过一个zsh git prompt,git status在开大仓库时会有性能问题,这个无解,因为git status实现就是dfs目录。v1和v2格式影响某些特定状态的解析。我记得当时做实验,v1输出相对更短一些。

当时结论是使用git --no-optional-locks,性能可以提升大约10%。

我也用的 zsh,很大一个原因就是 zsh 支持异步提示符。

很大的仓库,比如 emacs, git 这种,代码上了一定量级以后,用 v1 v2 一样慢。

所以我用了异步提示符来解决这个问题。先有 prompt, 然后 git 信息刷完后再更新一版 prompt.

已经用了五六年了,一直挺稳的。尤其在 msys2 下发挥了很大的作用(我 linux, windows, termux 用的都是这套配置)。

1 个赞

我觉得这里异步不好,如果用户执行的是git无关命令就罢了,如果用户刚执行完一条git命令或者调用了git的命令,应该让用户立刻看到反馈,不然prompt里的git status就成了一个存在不确定时延的快照。

用starship好多年,感觉挺好,速度还快。异步也没啥问题,延时没那么大

一个存在不确定时延的快照

这点我认为没你想的那么大的影响。

因为即使你使用同步的 prompt,这个 git 状态仍然是一个快照。仍然避免不了在其他 ide/ai agent/terminal 对当前仓库的影响。

其次如果用户操作了 git,那么对 git 状态应该是有个预期的。我的实践是,这个状态会在我敲完命令前刷新成功。

另外第一次有了 cache 后,后面速度也是比较快的。

这个要看具体场景,我平时操作最多的就是linux代码库,乱的时候第一次等个几十秒是常有的事。而且我一般通过看prompt确定命令有没有成功让仓库进入我期望的状态。

至于多人操作这个场景,一般情况下也并不会出现多个开发者访问同一个git仓库目录。