bash 如何在终端右侧显示git状态信息?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7697889/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to show git status info on the right side of the terminal?
提问by JJD
Do you know if it is possible to configure the bash promptto show the git status/ branch infoon the right side as zshcan do? This randomly screen shot from the internet shows what I mean.
您知道是否可以像zsh一样配置bash 提示以在右侧显示git 状态/分支信息吗?这张来自互联网的随机屏幕截图显示了我的意思。


回答by ZyX
Try the following:
请尝试以下操作:
PS1='$(printf "%*s\r%s" $(( COLUMNS-1 )) "[$(git branch 2>/dev/null | grep '^*' | sed s/..//)] $(date +%H:%M:%S)" "heipei@wavefront:$PWD$ ")'
Note that you'll never get behavior that exactly matches zsh one with bash only. In the above case I see the following differencies:
请注意,您永远不会得到仅与 bash 完全匹配的行为。在上述情况下,我看到以下差异:
- Right part of the prompt is not cleared when you run a command (
accept-lineevent in terms of zsh). - Right part of the prompt will be cleared if you type something and then press
<C-u>or<BS>. - Right part of the prompt won't be restored if you type something over it and then delete the text.
- Right part of the prompt won't disappear if you type something over it, though text in this part will be overwritten.
- 运行命令时,提示的右侧部分不会被清除(
accept-linezsh 中的事件)。 - 如果您键入内容然后按
<C-u>或,则提示的右侧部分将被清除<BS>。 - 如果您在上面键入一些内容然后删除文本,则不会恢复提示的右侧部分。
- 如果你在上面输入一些东西,提示的右侧部分不会消失,尽管这部分的文本会被覆盖。
回答by Tom Hale
The code below will create a prompt which looks like:
下面的代码将创建一个提示,如下所示:
It's non-trival to do this in bashdue to:
bash由于以下原因,这样做并不容易:
- Readline mode string takes up characters before the prompt is printed, meaning that the
printfsolutions won't work in some cases. Because of this: - Removing all ANSI CSI codes(eg colours) to correctly calculate the length of the printable right-hand-side prompt
- Needing to use
__git_ps1to deal withgitedge cases __git_ps1only outputting colour in certain circumstances, and only inside$PS1- Allowing colour in the
__git_ps1output while removing the\[and\]characters from its output (which can't be nested) - Wrapping the whole RHS prompt in
\[and\]to ensure that the prompt doesn't do weird things when browsing / editing / completing commands
- Readline 模式字符串在打印提示之前占用字符,这意味着该
printf解决方案在某些情况下不起作用。因为这: - 删除所有ANSI CSI 代码(例如颜色)以正确计算可打印右侧提示的长度
- 需要用来
__git_ps1处理git边缘情况 __git_ps1仅在某些情况下输出颜色,并且仅在内部$PS1- 在从
__git_ps1输出中删除\[和\]字符时允许输出中的颜色(不能嵌套) - 包装整个 RHS 提示
\[并\]确保提示在浏览/编辑/完成命令时不会做奇怪的事情
#!/bin/bash
# _options=$(shopt -op); set -exu # Save and set shell options for testing
##################
# Set the prompt # Sourced from .bashrc
##################
# Select git info displayed, see /usr/lib/git-core/git-sh-prompt for more
export GIT_PS1_SHOWCOLORHINTS=1 # Make pretty colours inside $PS1
export GIT_PS1_SHOWDIRTYSTATE=1 # '*'=unstaged, '+'=staged
export GIT_PS1_SHOWSTASHSTATE=1 # '$'=stashed
export GIT_PS1_SHOWUNTRACKEDFILES=1 # '%'=untracked
export GIT_PS1_SHOWUPSTREAM="verbose" # 'u='=no difference, 'u+1'=ahead by 1 commit
export GIT_PS1_STATESEPARATOR='' # No space between branch and index status
export GIT_PS1_DESCRIBE_STYLE="describe" # Detached HEAD style:
# describe relative to older annotated tag (v1.6.3.1-13-gdd42c2f)
# contains relative to newer annotated tag (v1.6.3.2~35)
# branch relative to newer tag or branch (master~4)
# default exactly eatching tag
# Sets prompt like:
# ravi@boxy:~/prj/sample_app[exit]$ master*% u= | 30 Apr 22:27
_set_bash_prompt() {
# Set left hand side of the prompt
PS1="\u@\h:\w$ "
#
# Git status
#
# Save current state of user shopt settings promptvars and extglob
local user_shopt
user_shopt=$(shopt -p promptvars extglob)
# __git_ps1 usually returns literal text "${__git_ps1_branch_name}" rather
# than the contained branch name, eg "master". This prevents calculating
# the length of the printable characers in the RHS string (used to move the
# cursor that many columns left from the terminal's right edge.) However if
# "shopt promptvars" is unset, __git_ps1 it will include the dereferenced
# branch name instead.
shopt -qu promptvars
# extglob is required for the ${variable//@(pattern)/} replacements
shopt -qs extglob
# Allow disabling git status and no error if __git_ps1 undefined
if [[ ! -v _disable_git_prompt && $(type -t __git_ps1 2>/dev/null) == function ]]; then
# __git_ps1 will only make pretty colours inside $PS1
local old_PS1=$PS1
__git_ps1 "" "" "%s" # force colour; no default round bracket (decorations)
# Strip "\[" and "\[": non-printable character markers. __git_ps1 outputs
# them however the whole of the RHS prompt needs to be included in these
# markers, and they can't be nested.
git=${PS1//@(\@(\[|\]))/}
PS1=$old_PS1
fi
#
# Right hand side of prompt
#
local rhs="" # String to be printed on the right hand side of terminal
# Create a string like: "25 Apr 13:15"
local date_time
printf -v date_time "%(%e %b %H:%M)T" -1 # -1 is current time
# Format the RHS prompt
[[ -n $git ]] && rhs="$git | " #"
rhs+="\e[0;1;31m${date_time}"
# Strip ANSI CSI commands (eg colours) to enble counting the length of
# printable characters, giving offset of cursor from terminal RHS edge (from
# https://www.commandlinefu.com/commands/view/12043/remove-color-special-escape-ansi-codes-from-text-with-sed)
# Neither bash not sed support lookbehind zero-length assertions, so it's not
# possible to ignore "\e", (ie a literal '\' followed by a literal 'e'), yet
# still remove "\e" (ie ESC)
local rhs_printable=${rhs//@(\@(\[|]|[Ee]\[*([0-9;])[a-zA-Z]))/}
# or, in using sed (but requires exec):
# local rhs_printable=$(sed -e 's,\[][]\|\[Ee]\[\([0-9;]\)*[A-Za-z],,g' <<< "$rhs")
# Reference: https://en.wikipedia.org/wiki/ANSI_escape_code
local Save='\e[s' # Save cursor position
local Rest='\e[u' # Restore cursor to save point
# Save cursor position, jump to (right hand edge minus N columns) where N is
# the length of the printable RHS string. Print the RHS string, then return
# to the saved position and print the LHS prompt.
# Note: "\[" and "\]" are used so that bash can calculate the number of
# printed characters so that the prompt doesn't do strange things when
# command line editing/browsing/completion. Ensure that these are not nested.
PS1="\[\e[0m${Save}\e[$((COLUMNS - ${#rhs_printable}))G${rhs}${Rest}\]${PS1}"
eval "$user_shopt"
}
# eval "$_options"; unset _options # Restore previous shell options from line 2
回答by uli42
Today I built something like that in the following manner. Thorough testing has not been made yet...
今天我用以下方式构建了类似的东西。还没有进行彻底的测试......
preprompt() {
rc=$?
c=31
[ $rc -eq 0 ] && c=32
PS1="\[$(color $c)\]$rc\[$(color 0)\] \t \w $ "
# right "prompt"
# We cannot use $COLUMNS here, since in new shells the first prompt
# will get garbled then. Seems like the correct value of COLUMNS is
# in the shell init.
printf "%`tput cols`s`tput cr`" "${USER}@${HOST}"
}
PROMPT_COMMAND=preprompt
回答by c00kiemon5ter
One way would be to use tputto count the columnsof your terminal, and subtrack the number of chars that are going to be printed left and right, then use that number as the number of spaces between the left and right text. Use printfto construct the line.
一种方法是使用tput计算终端的列数,并子跟踪将要左右打印的字符数,然后使用该数字作为左右文本之间的空格数。使用printf构造线。
quick example:
快速示例:
left="[${status}]\u@\h:\w$ "
right="$(git symbolic-ref HEAD) $(date +%T)"
spaces="$(( $(tput cols) - ${#left} - ${#right} ))"
export PS1="$(printf "%s%${spaces}s\n" "$left" "$right")"

