bash 提示符中的 __git_ps1 (当前分支)总是以某种方式过时

bash 提示符中的 __git_ps1 (当前分支)总是以某种方式过时

我的PS1在我的~/.bash_profile

export PS1="\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]`__git_ps1`\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$ "

(抱歉,我的颜色代码没有任何别名,我使用在线编辑器创建了此提示)

这有点混乱,但产生了一个非常好的提示:在此输入图像描述

如果我切换,当前显示的分支总​​是错误的

错误的分支

我不知道为什么会发生这种情况。如果我单独运行该命令,我会得到正确的值。

$ echo `__git_ps1`
(dev)

如果我获取 .bash_profile,新值将会出现(但下次切换时会出错)。难道我做错了什么?

答案1

export PS1="…`__git_ps1`…"

使用`__git_ps1`内部双引号,此命令运行命令__git_ps1并将其输出(和其他周围文本)分配给变量PS1。因此,您的提示显示的是.bash_profile执行时确定的分支。

__git_ps1每次 bash 显示提示时都需要运行。 (实际上,在 git 信息发生更改之前,您不需要再次运行它,但这很难检测到。)有两种方法可以做到这一点。

  • `__git_ps1`在变量中包含文字文本PS1。确保 bash 配置为对提示字符串执行 shell 扩展,使用promptvars选项打开;默认情况下就是这种情况,但可以使用 关闭它shopt -u promptvars

    PS1='\n\[…\]$(__git_ps1)\[…\]\$ '
    
  • 通过从以下位置运行命令来更新提示内容PROMPT_COMMAND多变的。

    update_PS1 () {
      PS1="\\n\\[…\\]$(__git_ps1)\[…\]\\$ "
    }
    shopt -u promptvars
    PROMPT_COMMAND=update_PS1
    

顺便说一句,提示符是 shell 配置,而不是全局设置,所以你应该将其设置为~/.bashrc,而不是~/.bash_profile

答案2

这只是一个简单的引用问题。如果必须使用反引号,请将其更改`__git_ps1`为,或者: 。\$(__git_ps1)\`__git_ps1\`

为了说服自己,只需将 PS1 更改为(如果您想完全恢复到之前的设置,请打开一个新的 shell 实例):

$ PS1="$(date) >"
Thu Nov 26 20:02:34 EST 2015 >_

唯一的问题是它会不是更新(等待几秒钟按 Enter 键)。

但这会:

$ PS1="\$(date) >"
Thu Nov 26 20:06:20 EST 2015
Thu Nov 26 20:06:25 EST 2015

就这样。写exit。 (更新您的提示)回去工作。

答案3

http://mediadoneright.com/content/ultimate-git-ps1-bash-prompt

注意底部他们没有设置:

导出 PS1="\n... `__git_ps1 ...

将其设置为这个恶心的作品:

export PS1=$IBlack$Time12h$Color_Off'$(git branch &>/dev/null;\

if [ $? -eq 0 ]; then \
  echo "$(echo `git status` | grep "nothing to commit" > /dev/null 2>&1; \
  if [ "$?" -eq "0" ]; then \
    # @4 - Clean repository - nothing to commit
    echo "'$Green'"$(__git_ps1 " (%s)"); \
  else \
    # @5 - Changes to working tree
    echo "'$IRed'"$(__git_ps1 " {%s}"); \
  fi) '$BYellow$PathShort$Color_Off'\$ "; \
else \
  # @2 - Prompt when not in GIT repo
  echo " '$Yellow$PathShort$Color_Off'\$ "; \
fi)'

其相关位是“$(__git_ps1”(%s)”或“$(__git_ps1”{%s}”

答案4

我觉得现有的答案没有准确/清楚地传达根本原因。尽管它们可能会导致找到解决方案,但我不确定它们是否能教会观众应该吸取的教训。这个答案主要是从帮助观众达成理解的角度来回答。

快速回答

我在写完原始回复后想到了这种方法,因此将详细回复留在下面,但这是快速答案。

您的PS1值将存储为替换版本,因为输入期间未使用转义。您可以像这样验证它:

原来的:

chadb@Chad-2in1 ~/code/videospeed-refactoring (master) 21:12:45  
$ export PS1="\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]`__git_ps1`\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$ "

chadb@Chad-2in1 ~/code/videospeed-refactoring (master) 21:13:03
$ env |grep PS1
PS1=\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\] (master)\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\$

逃脱

chadb@Chad-2in1 ~/code/videospeed-refactoring (master) 21:19:42
$ export PS1="\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]\`__git_ps1\`\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$ "

chadb@Chad-2in1 ~/code/videospeed-refactoring (master) 21:20:02
$ env |grep PS1
PS1=\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]`__git_ps1`\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\$

如您所见,__git_ps1在第二个版本中保留了下来,而在第一个版本中,它显示了输入时 __get_ps1 的值。

继续我原来的答案。

解释

问题是,当 shell 执行命令时,它会在插入之前执行替换,因此会插入替换值而不是原始命令。

为了解决这个问题,您只需\在存储之前使用转义字符来防止替换。这会导致命令的未转义可执行版本存储在内存中,以便 bash 会根据每个输入的命令重新评估它。

未转义的命令导致它在存储在内存中之前被执行和替换,这反过来又导致它仅在~/.bash_profile第一次执行/获取时显示命令。

解决方案

原始命令

export export PS1="\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]`__git_ps1`\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$ "

$PS1 的原始存储值

包括字符串文字“ (master) ”

\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\] (master) \[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$

重要提示:为了避免混淆,该存储值仅代表对__git_ps1

固定命令

export export PS1="\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]\`__git_ps1\`\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$ "

$PS1 的固定存储值

这次命令保持不变,以便在下次执行时将其替换。

"\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]`__git_ps1`\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$ "

反引号不相关 - 证明

正如其他答案所提到的,您可以使用 $() 提供反引号,但是,这与此问题的根本原因无关。这些问题不应混为一谈。

老了[不是问题]

`__git_ps1`

新[不是问题]

$(__git_ps1)

原始命令没有反引号,未转义[不是问题,同样有问题]

export export PS1="\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]$(__git_ps1)\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$ "

不带反引号的原始命令转义[以相同方式修复,带转义]

export export PS1="\\n\[\033[38;5;246m\]\u@\[\033[38;5;245m\]\h\[\033[38;5;15m\] \[\033[38;5;28m\]\w\[\033[38;5;15m\]\[\033[38;5;2m\]\$(__git_ps1)\[\033[38;5;15m\] \[\033[38;5;90m\]\t\[\033[38;5;15m\] \[\033[38;5;232m\]\[\033[38;5;15m\] \n\[\033[38;5;0m\]\\$ "

简化证明

为了简化这一点,让我们通过创建自定义 shell 命令来删除许多变量,该命令执行与 bash 类似的操作,只需使用文件sub_demo作为存储介质将当前工作目录回显到控制台以显示中间状态。

未转义的 $PWD [问题]

[07:18 PM] ~/code $ echo $PWD
/c/Users/chadb/code
[07:36 PM] ~/code $ echo $PWD > sub_demo
[07:36 PM] ~/code $ cat sub_demo
/c/Users/chadb/code

请注意, 的替换值$PWD已存储在文件中,从而破坏了用于检索该目录的命令的信息。如果我们只是从现在开始读出 sub_demo 的内容,它总是会产生相同的渲染值/c/Users/chadb/code

这样做很愚蠢,但为了说明目的,我将从多个位置读取文件的内容,以证明这实际上不会更改存储在其中的信息。

[08:01 PM] ~/code $ cat ~/code/sub_demo 
/c/Users/chadb/code
[08:01 PM] ~ $ cat ~/code/sub_demo
/c/Users/chadb/code
[08:01 PM] ~/code/sub_demo_folder $ cat ~/code/sub_demo 
/c/Users/chadb/code

请注意,无论文件夹如何,该值都不会更改。的内容~/code/sub_demo从现在开始/c/Users/chadb/code直到我们更改为止。

逃脱 $PWD [解决方案]

现在考虑是否$通过转义字符\$

[08:06 PM] ~/code $ echo \$PWD > sub_demo
[08:06 PM] ~/code $ cat sub_demo
$PWD

正如您所看到的,该值现在存储为命令 - 与查找工作目录所需的命令相同。这有什么用?好吧,bash 会执行 $PS1 存储的值。让我们通过在开头添加 echo 来将文件的内容转换为可执行文件来模拟这一点。

为了完整性和对比,我将展示未转义和转义的版本。

[未逃脱]

[08:06 PM] ~/code $ echo "echo $PWD" > sub_demo
[08:11 PM] ~/code $ cat sub_demo
echo /c/Users/chadb/code

[逃脱]

[08:11 PM] ~/code $ echo "echo \$PWD" > sub_demo
[08:13 PM] ~/code $ cat sub_demo
echo $PWD

这还没有那么令人兴奋,但当你执行输出时真正的魔法就会发生。

最终简化证明

未转义 - 不起作用
[08:15 PM] ~/code $ echo "echo $PWD" > sub_demo
[08:15 PM] ~/code $ cat sub_demo
echo /c/Users/chadb/code
[08:15 PM] ~/code $ cd sub_demo_folder/
[08:15 PM] ~/code/sub_demo_folder $ cat ~/code/sub_demo | bash -
/c/Users/chadb/code
[08:15 PM] ~/code/sub_demo_folder $ cd ~
[08:15 PM] ~ $ cat ~/code/sub_demo | bash -
/c/Users/chadb/code
[08:15 PM] ~ $ cd code
[08:15 PM] ~/code $ cat ~/code/sub_demo | bash -
/c/Users/chadb/code

观察输出值为总是 /c/Users/chadb/code

逃脱 - 有效
[08:15 PM] ~/code $ echo "echo \$PWD" > sub_demo
[08:15 PM] ~/code $ cat sub_demo
echo $PWD
[08:16 PM] ~/code $ cd sub_demo_folder/
[08:19 PM] ~/code/sub_demo_folder $ cat ~/code/sub_demo | bash -
/c/Users/chadb/code/sub_demo_folder
[08:19 PM] ~/code/sub_demo_folder $ cd ~
[08:19 PM] ~ $ cat ~/code/sub_demo | bash -
/c/Users/chadb
[08:19 PM] ~ $ cd code/
[08:19 PM] ~/code $ cat ~/code/sub_demo | bash -
/c/Users/chadb/code

现在观察一下,每次执行命令时都会显示正确的当前的根据需要的目录。

相关内容