PS1 和 PROMPT_COMMAND 有什么区别?

发布于 2024-09-06 14:47:44 字数 420 浏览 8 评论 0原文

查看 这个很棒的线程 我注意到

PS1="Blah Blah Blah"

有些示例使用了某些示例

PROMPT_COMMAND="Blah Blah Blah"

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

While taking a look at this awesome thread I noticed that some examples use

PS1="Blah Blah Blah"

and some use

PROMPT_COMMAND="Blah Blah Blah"

(and some use both) when setting the prompt in a Bash shell. What is the difference between the two? A Stack Overflow search and even a bit of broader Google searching aren't getting me results, so even a link to the right place to look for the answer would be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

会发光的星星闪亮亮i 2024-09-13 14:47:44

PROMPT_COMMAND 可以包含普通的 Bash 语句,而 PS1 变量可以变量中还包含特殊字符,例如表示主机名的“\h”。

例如,这是我的 Bash 提示符,它同时使用 PROMPT_COMMAND 和 PS1。 PROMPT_COMMAND 中的 Bash 代码计算出您可能所在的 Git 分支,并在提示符下显示该分支,以及上次运行进程的退出状态、密码

变量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 $

Update

阅读评论和 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

PROMPT_COMMAND can contain ordinary Bash statements whereas the PS1 variable can also contain the special characters, such as '\h' for hostname, in the variable.

For example, here is my Bash prompt that uses both PROMPT_COMMAND and PS1. The Bash code in PROMPT_COMMAND works out what Git branch you might be in and displays that at the prompt, along with the exit status of the last run process, hostname and basename of the pwd.

The variable RET stores the return value of the last executed program. This is convenient to see if there was an error and the error code of the last program I ran in the terminal. Note the outer ' surrounding the entire PROMPT_COMMAND expression. It includes PS1 so that this variable is reevaluated each time the PROMPT_COMMAND variable is evaluated.

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";'

Example output looks like this in a non-Git directory:

sashan@dhcp-au-122 Documents  $ false
sashan@dhcp-au-122 Documents  1 $

And in a Git directory you see the branch name:

sashan@dhcp-au-122 rework mybranch $

Update

After reading the comments and Bob's answer, I think that writing it as he describes is better. It's more maintainable than what I originally wrote above, where the PS1 variable is set inside the PROMPT_COMMAND, which itself is a super complicated string that is evaluated at runtime by Bash.

It works, but it's more complicated than it needs to be. To be fair, I wrote that PROMPT_COMMAND for myself about 10 years ago and it worked and didn't think too much about it.

For those curious as to how I've amended my things, I've basically put the code for the PROMPT_COMMAND in a separate file (as Bob described) and then echo the string that I intend to be 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"

And in my .bashrc file:

function prompt_command {
    RET=$?
    export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
幸福还没到 2024-09-13 14:47:44

从 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时我可以用它。

From the GNU Bash documentation page (Bash Reference Manual):

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

I never used it, but I could have used this back when I only had sh.

等风来 2024-09-13 14:47:44

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

将其放入您的 .bashrc 文件中:

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 和命令历史记录工作正常。

The difference is that PS1 is the actual prompt string used, and PROMPT_COMMAND is a command that is executed just before the prompt. If you want the simplest, most flexible way of building a prompt, try this:

Put this in your .bashrc file:

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

Then write a script (Bash, Perl, or Ruby: your choice), and place it in file ~/bin/bash_prompt.

The script can use any information it likes to construct a prompt. This is much simpler, IMO, because you don't have to learn the somewhat baroque substitution language that was developed just for the PS1 variable.

You might think that you could do the same by simply setting PROMPT_COMMAND directly to ~/bin/bash_prompt, and setting PS1 to the empty string.

This at first appears to work, but you soon discover that the readline code expects PS1 to be set to the actual prompt, and when you scroll backwards in history, things get messed up as a result.

This workaround causes PS1 to always reflect the latest prompt (since the function sets the actual PS1 variable used by the invoking instance of the shell), and this makes readline and command history work fine.

暮倦 2024-09-13 14:47:44

来自 man bash

PROMPT_COMMAND

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

PS1

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

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

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

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

PROMPT_COMMAND='sync'

From man bash:

PROMPT_COMMAND

If set, the value is executed as a command prior to issuing each primary prompt.

PS1

The value of this parameter is expanded (see PROMPTING below) and used as the primary prompt string. The default value is ''\s-\v\$ ''.

If you simply want to set the prompt string, using PS1 alone is enough:

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

If you want to do something else just before printing the prompt, use PROMPT_COMMAND. For example, if you want to sync cached writes to disk, you can write:

PROMPT_COMMAND='sync'
凉城 2024-09-13 14:47:44

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

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

我的意思是,如果有人想要设置 另一个 变量,其范围超出提示:根据 shell,该变量可能需要首先在 $PS1 之外声明 或(最坏的情况)在调用 $PS1 之前可能需要先在 FIFO 上等待一些东西(并在 $PS1 末尾再次武装) ); \u \h 可能会引起一些麻烦,特别是当您使用一些奇特的正则表达式时;但除此之外:通过使用 $PS1 中的命令替换(也许在极端情况下,显式子 shell),可以完成任何 PROMPT_COMMAND 可以完成的事情吗?

正确的?

Yeah, so to try to really nail this down:

  • PROMPT_COMMAND is a handy Bash convenience variable/function, but there is, strictly speaking, nothing that cannot also be done using PS1 alone, correct?

I mean, if one wants to set another variable with scope outside the prompt: depending on the shell, that variable would probably need to be declared first outside $PS1 or (worst case) one might have to get fancy with something waiting on a FIFO prior to calling $PS1 (and armed again at the end of $PS1); the \u \h might cause some trouble, particularly if you're using some fancy regex; but otherwise: one can accomplish anything PROMPT_COMMAND can by using command substitution within $PS1 (and, maybe in corner cases, explicit subshells)?

Right?

葬花如无物 2024-09-13 14:47:44

我花了很多时间在这上面,我只是想分享对我有用的东西。我查看了很多关于 PROMPT_COMMAND 和 PS1 的 SO 帖子,并尝试了单引号、双引号、调用函数的许多组合...如果不打印控制字符或文字扩展但未处理,我无法每次更新提示提示字符串,或者不只是在 PROMPT_COMMAND 中设置 PS1,我们建议不要这样做。我的问题是设置包含控制字符的变量(颜色);这些必须在 PS1 中的变量名称之后进行硬编码。 PROMPT_COMMAND 设置为设置变量的函数,并且它们在双引号 PS1 字符串中使用(转义)。这是针对电力线风格的提示,该提示会随着每个命令而改变颜色。

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\] "

这应该能让你继续前进!

I spent so much time on this I just wanted to share what worked for me. I looked at a lot of the SO posts about PROMPT_COMMAND and PS1 and tried many combinations of single quotes, double quotes, calling functions... I could not get the prompt to update each time without printing control characters or the literal expanded but not processed prompt string, or without just setting PS1 in PROMPT_COMMAND as we are advised not to do. My problem was setting variables (colors) that contained control characters; these had to be hard-coded after the variable name in PS1. PROMPT_COMMAND is set to a function that sets variables and they are used (escaped) in a double-quoted PS1 string. This is for a powerline-style prompt that changes colors with each command.

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\] "

That ought to get you going!

鲜血染红嫁衣 2024-09-13 14:47:44

不同之处在于,

  • 如果您从 PROMPT_COMMAND 输出不完整的行,它会搞砸您的 Bash 提示符
  • PS1 替换 \H 和朋友
  • PROMPT_COMMAND 运行其内容,PS1 使用其内容作为提示符。

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

The difference is that

  • if you output an incomplete line from PROMPT_COMMAND, it will screw your Bash prompt
  • PS1 substitutes \H and friends
  • PROMPT_COMMAND runs its contents, and PS1 uses its contents as the prompt.

PS1 does variable expansion and command substitution at each prompt. There isn't any need to use PROMPT_COMMAND to assign a value to PS1 or to run arbitrary code. You can easily do export PS1='$(uuidgen) $RANDOM' once in file .bash_profile. Just use single quotes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文