在查看这个神奇的帖子时,我注意到一些示例使用了
PS1="Blah Blah Blah"
还有一些使用
PROMPT_COMMAND="Blah Blah Blah"
在设置Bash shell提示符时,有些人使用单引号,有些人使用双引号。这两者之间有什么区别?我在Stack Overflow上搜索了一下,甚至进行了更广泛的Google搜索,都没有找到答案,所以即使给出一个正确的查找答案的链接也会很感激。
PROMPT_COMMAND可以包含普通的Bash语句,而PS1变量也可以包含特殊字符,比如'\h'代表主机名,加入到变量中。
例如,这是我使用了PROMPT_COMMAND和PS1的Bash提示符。在PROMPT_COMMAND中的Bash代码确定你可能处于的Git分支,并在提示符中显示它,还有最后运行的进程的退出状态、主机名和pwd的基本名称。
变量RET存储上一个执行的程序的返回值。这很方便,可以查看是否出现错误以及终端中执行的最后一个程序的错误代码。请注意整个PROMPT_COMMAND表达式外面的单引号。它包括PS1,以便每次评估PROMPT_COMMAND变量时重新评估该变量。
PROMPT_COMMAND='RET=$?;\
BRANCH="";\
ERRMSG="";\
if [[ $RET != 0 ]]; then\
ERRMSG=" $RET";\
fi;\
if git branch &>/dev/null; then\
BRANCH=$(git branch 2>/dev/null | grep \* | cut -d " " -f 2);\
fi;
PS1="$GREEN\u@\h $BLUE\W $CYAN$BRANCH$RED$ERRMSG \$ $LIGHT_GRAY";'
在非Git目录中,示例输出看起来像这样:
sashan@dhcp-au-122 Documents $ false
sashan@dhcp-au-122 Documents 1 $
在Git目录中,你可以看到分支名称:
sashan@dhcp-au-122 rework mybranch $
在阅读了评论和Bob的答案之后,我认为按照他所描述的编写代码更好。这比我上面最初编写的方式更易于维护,因为PS1变量是在PROMPT_COMMAND内部设置的,而PROMPT_COMMAND本身是一个超级复杂的字符串,在Bash中运行时进行评估。
它能够工作,但比必要的要复杂。公平地说,我大约10年前为自己编写了那个PROMPT_COMMAND,它能够正常运行,我没有过多考虑。
对于那些好奇我如何修改自己的东西的人,我基本上将PROMPT_COMMAND的代码放在一个单独的文件中(如Bob所述),然后输出我打算成为PS1的字符串:
GREEN="\[\033[0;32m\]"
CYAN="\[\033[0;36m\]"
RED="\[\033[0;31m\]"
PURPLE="\[\033[0;35m\]"
BROWN="\[\033[0;33m\]"
LIGHT_GRAY="\[\033[0;37m\]"
LIGHT_BLUE="\[\033[1;34m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_RED="\[\033[1;31m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
YELLOW="\[\033[1;33m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour
if [ -z $SCHROOT_CHROOT_NAME ]; then
SCHROOT_CHROOT_NAME=" "
fi
BRANCH=""
ERRMSG=""
RET=$1
if [[ $RET != 0 ]]; then
ERRMSG=" $RET"
fi
if which git &>/dev/null; then
BRANCH=$(git branch 2>/dev/null | grep \* | cut -d " " -f 2)
else
BRANCH="(git not installed)"
fi
echo "${GREEN}\u@\h${SCHROOT_CHROOT_NAME}${BLUE}\w \
${CYAN}${BRANCH}${RED}${ERRMSG} \$ $RESTORE"
在我的.bashrc文件中:
function prompt_command {
RET=$?
export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
来自GNU Bash文档页面(Bash参考手册):
PROMPT_COMMAND
If set, the value is interpreted as a command to execute before
the printing of each primary prompt ($PS1).
我从未使用过它,但当我只有sh时我可能会用到它。
PS1
是实际使用的提示字符串,而PROMPT_COMMAND
是在提示之前执行的命令。如果你想要最简单、最灵活的方式来构建提示符,请尝试以下操作:function prompt_command {
export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command
然后编写一个脚本(Bash,Perl或Ruby:由您选择),并将其放置在文件 ~/bin/bash_prompt 中。
该脚本可以使用任何信息来构建提示符。在我看来,这要简单得多,因为您不必学习为PS1
变量开发的有点古怪的替换语言。
您可能认为只需将PROMPT_COMMAND
直接设置为~/bin/bash_prompt,并将PS1
设置为空字符串即可实现相同的功能。
这一开始似乎有效,但很快您就会发现readline代码希望将PS1
设置为实际提示符,当您向后滚动历史记录时,结果会混乱。
此解决方法使PS1
始终反映最新的提示符(因为该函数设置了由调用实例的shell使用的实际PS1
变量),这使得readline和命令历史记录正常工作。
PROMPT_COMMAND
中设置PS1
!应该在PROMPT_COMMAND
中设置变量并在PS1
中使用它们。否则,您将失去使用PS1
转义序列(如\u
或\h
)的能力。您必须在PROMPT_COMMAND
中重新创建它们。这可能是可行的,但是无法解决丢失\[
和\]
的问题,它们标记不可打印字符的开始和结束。这意味着您无法在不让终端混淆提示长度的情况下使用颜色。这会导致在编辑生成两行的命令时混淆readline
。最终你会在屏幕上看到一团乱麻。 - cevingPROMPT_COMMAND
会在打印 PS1
之前执行。我认为从 PROMPT_COMMAND
中设置 PS1
没有问题,因为在 PROMPT_COMMAND
完成后,shell 将打印经过修改的 PS1
,该修改是在 PROMPT_COMMAND
中完成的(或者在这种情况下,在 prompt_command
中完成)。 - Felipe Alvarezexport PS1='$(~/bin/bash_prompt)'
可以做同样的事情,但看起来更加清晰明了。 - pal来自bash手册
:
PROMPT_COMMAND
如果设置了该变量,其值将在发出每个主提示符之前作为命令执行。
PS1
该参数的值会被扩展(参见下面的“提示”),并用作主提示符字符串。默认值为''\s-\v\$ ''。
如果仅想设置提示符字符串,则仅需使用PS1
即可:
PS1='user \u on host \h$ '
PROMPT_COMMAND
。例如,如果您想将缓存的写入同步到磁盘上,可以编写以下内容:PROMPT_COMMAND='sync'
PS1
设置终端的标题,而不需要使用 PROMPT_COMMAND
。只需在 PS1
中包含设置标题的序列,并用 \[
和 \]
包装即可。 - dolmenPS1
中动态设置环境变量,但它只会在子shell中设置,所以你无法获取其值。但是你的示例很简单:PS1='$(sync)user \u on host \h$ '
。 - pal是的,为了真正掌握这个:
PROMPT_COMMAND
是一个方便的 Bash 变量/函数,但严格来说,也可以仅使用 PS1
来完成所有操作,对吗?我的意思是,如果想要 设置 具有提示范围之外作用域的 另一个 变量:取决于 shell,该变量可能需要在 $PS1
之外先声明,或者(最坏情况下)可能需要在调用 $PS1
之前使用某些等待 FIFO 的技巧(并在 $PS1
结束时再次启动);\u
\h
可能会引起一些麻烦,特别是如果您正在使用一些复杂的正则表达式;但除此之外:通过在 $PS1
中使用命令替换(和在某些角落情况下使用显式子 shell),可以完成任何 PROMPT_COMMAND
可以做到的事情?
对吗?
区别在于:
PROMPT_COMMAND
输出不完整的一行,会破坏你的Bash提示符。PS1
替换\H
等内容。PROMPT_COMMAND
运行其内容,PS1
使用其内容作为提示符。PS1
每次提示符时进行变量展开和命令替换。没有必要使用PROMPT_COMMAND
分配一个值给PS1
或者运行任意代码。你可以在文件.bash_profile
中只执行一次export PS1='$(uuidgen) $RANDOM'
。只需要使用单引号。
我花了很多时间研究这个问题,现在我想分享一下我的解决方案。我查看了许多关于PROMPT_COMMAND和PS1的SO帖子,并尝试了许多单引号、双引号、调用函数的组合……但是我无法在不打印控制字符或展开但未处理提示字符串的情况下每次更新提示,也不能像我们被建议的那样仅在PROMPT_COMMAND中设置PS1。我的问题在于设置包含控制字符的变量(颜色);这些必须在PS1中的变量名称后硬编码。PROMPT_COMMAND设置为一个设置变量并在双引号PS1字符串中使用(转义)它们的函数。这是一个类似于powerline风格的提示符,每次命令都会更改颜色。
icon1=#unicode powerline char like
#these: https://github.com/ryanoasis/powerline-extra-symbols#glyphs
icon2=#same
#array of ANSI colors. 2 for rgb mode then the rgb values
#then 'm' without '\]' control character. these are from
#the solarized theme https://ethanschoonover.com/solarized/
declare -a colors=(
"2;220;50;47m"
"2;203;75;22m"
"2;181;137;0m"
"2;133;153;0m"
"2;42;161;152m"
"2;38;139;210m"
"2;108;113;196m"
"2;211;54;130m"
"2;0;43;54m"
"2;7;54;66m"
"2;88;110;117m"
"2;101;123;131m"
"2;131;148;150m"
"2;147;161;161m"
)
#outside of vars set in PROMPT_COMMAND it's ok to have control chars
LEN=${#colors[@]}
BG="\[\e[48;"#set bg color
FG="\[\e[38;"#set fg color
TRANSP="1m\]"#transparency
BASE2="2;238;232;213m\]"#fg (text) color
myfunc(){
RAND=$(($RANDOM % $LEN))
COLOR1=${colors[$RAND]}
COLOR2=${colors[($RAND + 1) % $LEN]}
COLOR3=${colors[($RAND + 2) % $LEN]}
}
PROMPT_COMMAND=myfunc
#note double quotes and escaped COLOR vars followed by hard-coded '\]' control chars
PS1="$BG$TRANSP$FG\$COLOR1\]$icon1$BG\$COLOR1\]$FG$TRANSP$BG\$COLOR1\]$FG$BASE2
[username hard-coded in unicode] $BG\$COLOR2\]$FG\$COLOR1\]$icon2$BG\$COLOR2\]$FG$BASE2
\w $BG\$COLOR3\]$FG\$COLOR2\]$icon2$BG\$COLOR3\]$FG$BASE2 [more unicode]
\[\e[0m\]$FG\$COLOR3\]$icon2\[\e[0m\] "
那应该能让你开始了!
if git branch &>/dev/null ; then\
。它将stdout和stderr都重定向到/dev/null。http://www.tldp.org/LDP/abs/html/io-redirection.html - user184968PROMPT_COMMAND
。 - dolmenPROMPT_COMMAND
中在线改变PS1
不可取。这是完美有用的代码。与 Bob 的回答不同,PS1
变量已经正确构建。这使得基于您当前情况的更复杂的 bash 提示符成为可能。 - Christian WolfPROMPT_COMMAND
中构建PS1
没有任何意义,这只是一个不好的示例。应该在.bash_profile
中构建一次PS1
,并使用单引号而不是双引号,这样变量替换将在每个提示符中被计算。 - pal