PS1和PROMPT_COMMAND之间有什么区别

Jed*_*els 104 bash prompt

在看一下这个很棒的主题时,我注意到一些例子使用了

PS1="Blah Blah Blah"
Run Code Online (Sandbox Code Playgroud)

和一些用途

PROMPT_COMMAND="Blah Blah Blah"
Run Code Online (Sandbox Code Playgroud)

(和一些使用两者)在bash shell中设置提示时.两者有什么区别?SO搜索,甚至更广泛的谷歌搜索都没有得到我的结果,所以即使找到正确的地方寻找答案的链接将不胜感激.

sas*_*ang 66

PROMPT_COMMAND可以包含普通的bash语句,而PS1变量也可以在变量中包含特殊字符,例如主机名的'\ h'.

例如,这是我的bash提示符,它同时使用PROMPT_COMMAND和PS1.PROMPT_COMMAND中的bash代码计算出你可能在的git分支,并在提示符下显示,以及最后一个运行进程的退出状态,pwd的主机名和basename.变量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";'
Run Code Online (Sandbox Code Playgroud)

示例输出在非git目录中如下所示:

sashan@dhcp-au-122 Documents  $ false
sashan@dhcp-au-122 Documents  1 $ 
Run Code Online (Sandbox Code Playgroud)

在git目录中,您会看到分支名称:

sashan@dhcp-au-122 rework mybranch $ 
Run Code Online (Sandbox Code Playgroud)

更新

在阅读了评论和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"
Run Code Online (Sandbox Code Playgroud)

在我的.bashrc中

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
Run Code Online (Sandbox Code Playgroud)

  • 没有必要*export*`PROMPT_COMMAND`. (3认同)
  • 避免在 PROMPT_COMMAND 中设置 PS1 的原因之一是,如果这样做,您永远无法即时重置它,因为它会不断重置。并不常见,但发生时仍然令人困惑。 (3认同)
  • 您可以缩短其中一行: `if gitbranch &>/dev/null ; 然后\`。它将 stdout 和 stderr 重定向到 /dev/null。http://www.tldp.org/LDP/abs/html/io-redirection.html (2认同)
  • 我认为对于这个答案来说,ceving的评论非常正确:`不要在PROMPT_COMMAND中设置PS1!在PROMPT_COMMAND中设置变量并在PS1`中使用它们 (2认同)
  • 我看不出原因,为什么在“PROMPT_COMMAND”中在线更改“PS1”是不利的。这是完美的有用代码。与 Bob 的回答相反,“PS1”变量构建正确。这允许根据您的实际情况提供更复杂的 bash 提示。 (2认同)
  • @ChristianWolf 在 `PROMPT_COMMAND` 中构建 `PS1` 没有任何意义。这是一个如何不这样做的例子。在 `.bash_profile` 中构造一次 `PS1`,只需使用单引号而不是双引号,以便在每个提示期间评估变量替换。 (2认同)

小智 57

从GNU Bash文档页面:http: //www.gnu.org/software/bash/manual/bashref.html

PROMPT_COMMAND
    If set, the value is interpreted as a command to execute before
    the printing of each primary prompt ($PS1).
Run Code Online (Sandbox Code Playgroud)

我从来没有用过它,但是当我只有sh时我可以用它.


小智 46

区别在于PS1是使用的实际提示字符串,PROMPT_COMMAND是在提示之前执行的命令.如果您想要最简单,最灵活的方法来构建提示,请尝试以下方法:

把它放在你的.bashrc中:

function prompt_command {
  export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command
Run Code Online (Sandbox Code Playgroud)

然后编写一个脚本(bash,perl,ruby:您的选择),并将其放在〜/ bin/bash_prompt中.

该脚本可以使用它喜欢的任何信息来构造提示.这是一个更简单的IMO,因为您不必学习仅为PS1变量开发的有些巴洛克式替换语言.

您可能认为只需将PROMPT_COMMAND直接设置为〜/ bin/bash_prompt,并将PS1设置为空字符串即可.这开始似乎有效,但您很快就会发现readline代码期望PS1设置为实际提示,当您在历史记录中滚动后缀时,事情就会变得混乱.此解决方法使PS1始终反映最新提示(因为该函数设置了shell调用实例使用的实际PS1),这使得readline和命令历史记录正常工作.

  • 不要在`PROMPT_COMMAND`中设置`PS1`!在`PROMPT_COMMAND`中设置变量并在`PS1`中使用它们.否则你将无法使用`\ u`或`\ h`这样的`PS1`转义序列.你必须在`PROMPT_COMMAND`重新发明它们.这可能是可能的,但是不可能解决标记不可打印字符的开头和结尾的`\ [和`\]`的丢失.这意味着您不能使用颜色而不会使终端混淆提示的长度.当编辑产生两行的命令时,这会混淆`readline`.最后,你在屏幕上有一个很大的混乱. (15认同)
  • 在打印"PS1"之前执行`PROMPT_COMMAND`.我认为在`PROMPT_COMMAND`中设置`PS1`没有问题,因为在'PROMPT_COMMAND`完成后,shell将打印`PS1`,它是从`PROMPT_COMMAND`修改的(或者在这种情况下,在`prompt_command`里面)? (3认同)
  • 警告:PROMPT_COMMAND通常不应该用于直接将字符打印到提示符.打印在PS1外部的字符不会被Bash计算,这会导致它错误地放置光标并清除字符.使用PROMPT_COMMAND设置PS1或查看嵌入命令.([Arch Wiki Source](https://wiki.archlinux.org/index.php/Bash/Prompt_customization#PROMPT_COMMAND)) (3认同)
  • @ceving 确实如此!人们可以使用 PROMPT_COMMAND 来更改 PS1 的 **格式** 并获得两全其美的效果 (2认同)
  • 我不明白为什么每个人都尝试在PROMPT_COMMAND中做一些技巧而不是仅在PS1中使用命令替换```export PS1 ='$(〜/ bin / bash_prompt)'`''做同样的事情看起来很合理 (2认同)
  • @Ivanq事实上,`\[`只是`\001`的特定于提示符的别名,就像`\u`是`$USER`的特定于提示符的别名一样。 (2认同)

Cyk*_*ker 9

来自man bash:

PROMPT_COMMAND

如果设置,则在发出每个主要提示之前将该值作为命令执行.

PS1

扩展此参数的值(请参阅下面的PROMPTING)并将其用作主要提示字符串.默认值为''\ s-\v\$''.

如果您只想设置提示字符串,PS1单独使用就足够了:

PS1='user \u on host \h$ '
Run Code Online (Sandbox Code Playgroud)

如果您想在打印提示之前执行其他操作,请使用PROMPT_COMMAND.例如,如果要将缓存写入同步到磁盘,则可以编写:

PROMPT_COMMAND='sync'
Run Code Online (Sandbox Code Playgroud)