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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 00:56:13  来源:igfitidea点击:

How to show git status info on the right side of the terminal?

gitbashzshcommand-prompt

提问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 状态/分支信息吗?这张来自互联网的随机屏幕截图显示了我的意思。

Screen shot showing git status on the right side

在右侧显示 git status 的屏幕截图

回答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 完全匹配的行为。在上述情况下,我看到以下差异:

  1. Right part of the prompt is not cleared when you run a command (accept-lineevent in terms of zsh).
  2. Right part of the prompt will be cleared if you type something and then press <C-u>or <BS>.
  3. Right part of the prompt won't be restored if you type something over it and then delete the text.
  4. Right part of the prompt won't disappear if you type something over it, though text in this part will be overwritten.
  1. 运行命令时,提示的右侧部分不会被清除(accept-linezsh 中的事件)。
  2. 如果您键入内容然后按<C-u>或,则提示的右侧部分将被清除<BS>
  3. 如果您在上面键入一些内容然后删除文本,则不会恢复提示的右侧部分。
  4. 如果你在上面输入一些东西,提示的右侧部分不会消失,尽管这部分的文本会被覆盖。

回答by Tom Hale

The code below will create a prompt which looks like:

下面的代码将创建一个提示,如下所示:

bash prompt with git status on right

右侧带有 git status 的 bash 提示

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 with gitedge 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")"