我使用了两个轻量级的 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}"
