如何在终端的右侧显示git状态信息?

8
你知道是否可以配置bash提示符,使其像zsh一样在右侧显示git状态/分支信息吗?这是从互联网随机截取的屏幕截图,展示了我的意思。
4个回答

10

尝试以下方法:

PS1='$(printf "%*s\r%s" $(( COLUMNS-1 )) "[$(git branch 2>/dev/null | grep '^*' | sed s/..//)] $(date +%H:%M:%S)" "heipei@wavefront:$PWD$ ")'
请注意,仅使用bash无法完全匹配zsh的行为。在上述情况下,我看到以下差异:
  1. 当您运行命令(在zsh中是accept-line事件)时,提示符的右侧部分不会清除。
  2. 如果您键入任何内容,然后按<C-u><BS>,提示符的右侧部分将被清除。
  3. 如果您键入文字并将其删除,则不会恢复提示符的右侧部分。
  4. 如果您键入文字覆盖此部分,则提示符的右侧部分不会消失,但是该部分中的文本将被覆盖。

2
下面的代码将创建一个看起来像这样的提示:

bash prompt with git status on right

这在中做起来不容易,因为:
  • Readline模式的字符串会在提示符打印之前占用字符,这意味着在某些情况下,printf的解决方案将无法工作。因此:
  • 删除所有ANSI CSI代码(例如颜色),以正确计算可打印的右侧提示的长度
  • 需要使用__git_ps1来处理git边缘情况
  • __git_ps1仅在某些情况下输出颜色,并且仅在$PS1内部
  • 允许在__git_ps1输出中使用颜色,同时从其输出中删除\[\]字符(不能嵌套)
  • 将整个右侧提示包装在\[\]中,以确保在浏览/编辑/完成命令时提示不会出现奇怪的情况

#!/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

1
今天我用以下方式构建了类似的东西。目前还没有进行彻底的测试...
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

-1
一种方法是使用tput来计算您终端的列数,并减去将要打印的左右字符数,然后使用该数字作为左右文本之间空格的数量。使用printf构造行。
快速示例:
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")"

哦,我刚看到这是在同一行上。我不认为那个^会起作用。听起来更像是你需要更多地了解[n]curses的东西。 - c00kiemon5ter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接