PS1 和 PROMPT_COMMAND 有啥区别?

Posted

技术标签:

【中文标题】PS1 和 PROMPT_COMMAND 有啥区别?【英文标题】:What is the difference between PS1 and PROMPT_COMMAND?PS1 和 PROMPT_COMMAND 有什么区别? 【发布时间】:2011-03-04 18:04:25 【问题描述】:

在查看 this awesome thread 时,我注意到一些示例使用

PS1="Blah Blah Blah"

还有一些使用

PROMPT_COMMAND="Blah Blah Blah"

(有些人同时使用)在 Bash shell 中设置提示时。两者有什么区别? Stack Overflow 搜索,甚至更广泛的 Google 搜索都没有为我提供结果,因此即使是指向正确位置的链接来寻找答案也将不胜感激。

【问题讨论】:

【参考方案1】:

来自 GNU Bash 文档页面 (Bash Reference Manual):

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

我从来没有用过,但当我只有 sh 的时候我可以用回来。

【讨论】:

【参考方案2】:

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

例如,这是我的 Bash 提示符,它同时使用了 PROMPT_COMMAND 和 PS1。 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 $

更新

在阅读了 cmets 和 Bob 的答案后,我认为按照他的描述写它会更好。它比我最初在上面写的更易于维护,其中 PS1 变量设置在 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

【讨论】:

您可以缩短其中一行:if git branch &>/dev/null ; then\ 。它将 stdout 和 stderr 都重定向到 /dev/null。 tldp.org/LDP/abs/html/io-redirection.html 不需要导出 PROMPT_COMMAND. 我认为 ceving 的评论对于这个答案也非常正确:Don't set PS1 in PROMPT_COMMAND! Set variables in PROMPT_COMMAND and use them in PS1 我看不出原因,为什么在PROMPT_COMMAND 中在线更改PS1 是不利的。这是完美有用的代码。与 Bob 的回答相反,PS1 变量构造正确。这允许根据您的实际情况进行更复杂的 bash 提示。 @ChristianWolf 在PROMPT_COMMAND 内构造PROMPT_COMMAND 毫无用处。这是一个如何不做的例子。在.bash_profile 中构造一次PS1,只需使用单引号而不是双引号,以便在每次提示期间评估变量替换。【参考方案3】:

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

把它放在你的 .bashrc 文件中:

function prompt_command 
  export PS1=$(~/bin/bash_prompt)

export PROMPT_COMMAND=prompt_command

然后编写一个脚本(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 中使用它们。否则,您将失去使用PS1 转义序列的能力,例如\u\h。您必须在 PROMPT_COMMAND 中重新发明它们。这可能是可能的,但不可能解决\[\] 的丢失,它们标记了不可打印字符的开始和结束。这意味着您不能在不混淆终端提示长度的情况下使用颜色。这在编辑产生两行的命令时会混淆readline。最后,你的屏幕上出现了大混乱。 @ceving 没错!可以使用 PROMPT_COMMAND 更改 PS1 的格式,并获得两全其美 PROMPT_COMMAND 在打印PS1 之前执行。我发现从PROMPT_COMMAND 内部设置PS1 没有问题,因为在PROMPT_COMMAND 完成后,shell 将打印PS1,它是从PROMPT_COMMAND 修改的(或者在这种情况下,在prompt_command 内部)? 警告:通常不应使用 PROMPT_COMMAND 将字符直接打印到提示符。在 PS1 之外打印的字符不会被 Bash 计算在内,这会导致它错误地放置光标并清除字符。使用 PROMPT_COMMAND 设置 PS1 或查看嵌入命令。 (Arch Wiki Source) 我不明白为什么每个人都试图在 PROMPT_COMMAND 中做一些技巧而不是仅仅在 PS1 中使用命令替换 export PS1='$(~/bin/bash_prompt)' 做同样的事情错误看起来很正常【参考方案4】:

来自man bash

PROMPT_COMMAND

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

PS1

此参数的值被扩展(参见下面的 PROMPTING)并用作主要提示字符串。默认值为 ''\s-\v\$ ''。

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

PS1='user \u on host \h$ '

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

PROMPT_COMMAND='sync'

【讨论】:

你也可以从PS1设置终端的标题,而不需要PROMPT_COMMAND,因为设置标题的顺序可以包含在PS1中,用\[\]包裹. @dolmen 好的。那我们做点别的吧,比如动态设置一个环境变量。 @Cyker 您可以在PS1 中动态设置环境变量,它只会在子shell 中设置,因此您无法取回它的值。但你的例子是微不足道的PS1='$(sync)user \u on host \h$ '【参考方案5】:

区别在于

如果您从 PROMPT_COMMAND 输出不完整的行,它将破坏您的 Bash 提示符 PS1替补\H和朋友们 PROMPT_COMMAND 运行其内容,PS1 使用其内容作为提示。

PS1 在每个提示符处进行变量扩展和命令替换。无需使用PROMPT_COMMANDPS1 赋值或运行任意代码。您可以轻松地在文件.bash_profile 中执行一次export PS1='$(uuidgen) $RANDOM'。只需使用单引号。

【讨论】:

【参考方案6】:

是的,所以要尝试真正确定这一点:

PROMPT_COMMAND 是一个方便的 Bash 便利变量/函数,但严格来说,没有什么是不能单独使用 PS1 完成的,对吗?

我的意思是,如果想要set another 变量的范围在提示符之外:根据shell,可能需要首先在@987654323 之外声明该变量@ 或(最坏的情况)在调用 $PS1(并在 $PS1 结束时再次武装)之前,人们可能不得不对等待 FIFO 的东西有所期待; \u \h 可能会引起一些麻烦,特别是如果您使用一些花哨的正则表达式;但除此之外:PROMPT_COMMAND 可以通过在$PS1 中使用命令替换来完成任何事情(并且,也许在极端情况下,显式子shell)?

对吗?

【讨论】:

以上是关于PS1 和 PROMPT_COMMAND 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

[linux运维] 利用zabbix监控linux高危命令并发送告警

从 bash 接收切换到 zsh 后:“PROMPT_COMMAND=vim”附近的解析错误

Linux 利用 PROMPT_COMMAND 实现审计功能

如何在不修改 .git/index 的情况下运行 git status - 例如在 PROMPT_COMMAND 中

linux利用PROMPT_COMMAND实现命令审计

利用SHELL的PROMPT_COMMAND添加日志审计功能,实时记录任何用户的操作到日志文件中