如何扩展PS1?
我有一个 shell 脚本,它在多个目录中运行相同的命令(fgit)。对于每个目录,我希望它显示当前的提示符+将在那里运行的命令。如何获取与解码(扩展)PS1
对应的字符串?例如,我的默认 PS1 是
${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$(__git_ps1 ' (%s)')$
,我想回显结果提示 username@hostname:/path$
,最好(但不一定)使用漂亮的颜色。粗略地浏览一下 Bash 手册并没有找到任何明确的答案,而 echo -e $PS1
仅评估颜色。
I have a shell script that runs the same command in several directories (fgit). For each directory, I would like it to show the current prompt + the command which will be run there. How do I get the string that corresponds to the decoded (expanded) PS1
? For example, my default PS1 is
${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$(__git_ps1 ' (%s)')$
and I'd like to echo the resulting prompt username@hostname:/path$
, preferably (but not necessarily) with the nice colors. A cursory look at the Bash manual didn't reveal any definite answer, and echo -e $PS1
only evaluates the colors.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
从 Bash 4.4 开始,您可以使用
@P
扩展:首先,我使用
read -r
将提示字符串放入变量myprompt
中,并在此处引用-doc:要打印提示(因为如果是
PS1
则会被解释),请使用扩展${myprompt@P}
:(事实上有一些< code>\001 和
\002
字符,来自\[
和\]
,您在此处看不到,但如果您尝试编辑这篇文章,您就可以看到它们;如果您键入命令,您也会在终端中看到它们)。为了摆脱这些,Dennis Williamson 在 bash 邮件列表上发送的技巧是使用 read -e -p 以便这些字符由 readline 库解释:
这将提示用户,
myprompt
正确解释。对于这篇文章,Greg Wooledge 回答说,您不妨从字符串中删除
\001
和\002
。这可以通过如下方式实现:在这篇文章中,Chet Ramey 回答说,您也可以使用
set +o emacs +o vi
完全关闭行编辑。所以这也可以:Since Bash 4.4 you can use the
@P
expansion:First I put your prompt string in a variable
myprompt
usingread -r
and a quoted here-doc:To print the prompt (as it would be interpreted if it were
PS1
), use the expansion${myprompt@P}
:(In fact there are some
\001
and\002
characters, coming from\[
and\]
that you can't see in here, but you can see them if you try to edit this post; you'll also see them in your terminal if you type the commands).To get rid of these, the trick sent by Dennis Williamson on the bash mailing list is to use
read -e -p
so that these characters get interpreted by the readline library:This will prompt the user, with the
myprompt
correctly interpreted.To this post, Greg Wooledge answered that you might as well just strip the
\001
and\002
from the string. This can be achieved like so:To this post, Chet Ramey answered that you could also turn off line editing altogether with
set +o emacs +o vi
. So this will do too:开源软件的一大优点是源代码是开放的:-)
Bash 本身不提供此功能,但您可以使用各种技巧来提供子集(例如替换
\u
与$USER
等)。然而,这需要大量重复的功能,并确保代码与bash
将来所做的任何事情保持同步。如果您想获得提示变量的全部功能(并且您不介意编写一些代码(如果您介意的话,您为什么在这里?)),添加到 shell 本身很容易。
如果您下载
bash
的代码(我正在查看版本 4.2),则会有一个y.tab.c
文件,其中包含decode_prompt_string() 函数:
这是评估
PSx
变量以进行提示的函数。为了允许向 shell 本身的用户提供此功能(而不是仅由 shell 使用),您可以按照以下步骤添加内部命令evalps1
。首先,更改
support/mkversion.sh
,这样您就不会将其与“真正的”bash
混淆,并且 FSF 可以出于保修目的拒绝所有知识: -) 只需更改一行(我添加了-pax
位):其次,更改
builtins/Makefile.in
以添加新的源文件。这需要执行多个步骤。(a) 将
$(srcdir)/evalps1.def
添加到DEFSRC
的末尾。(b) 将
evalps1.o
添加到OFILES
的末尾。(c) 添加所需的依赖项:
第三,添加
builtins/evalps1.def
文件本身,这是运行evalps1
命令时执行的代码:其中包括 GPL 许可证(因为我从
exit.def
修改了它),最后有一个非常简单的函数来获取和解码PS1
。最后,只需在顶级目录中构建该东西:
出现的 bash 可执行文件可以重命名为 paxsh ,尽管我怀疑它会像它的祖先一样流行: -)
运行它,您可以看到它的实际效果:
当您将
PSx
变量之一放入提示符中时,回显$PS1
只是为您提供该变量,而evalps1
命令对其进行评估并输出结果。现在,当然,对
bash
进行代码更改以添加内部命令可能会被某些人认为是过大的,但是,如果您想要完美评估PS1
,这肯定是一个选项。One great advantage of open source software is that the source is, well, open :-)
Bash itself does not provide this functionality but there are various tricks you can use to provide a subset (such as substituting
\u
with$USER
and so on). However, this requires a lot of duplication of functionality and ensuring that the code is kept in sync with whateverbash
does in future.If you want to get all the power of prompt variables (and you don't mind getting your hands dirty with a bit of coding (and, if you do mind, why are you here?)), it's easy enough to add to the shell itself.
If you download the code for
bash
(I'm looking at version 4.2), there's ay.tab.c
file which contains thedecode_prompt_string()
function:This is the function that evaluates the
PSx
variables for prompting. In order to allow this functionality to be provided to users of the shell itself (rather than just used by the shell), you can follow these steps to add an internal commandevalps1
.First, change
support/mkversion.sh
so that you won't confuse it with a "real"bash
, and so that the FSF can deny all knowledge for warranty purposes :-) Simply change one line (I added the-pax
bit):Second, change
builtins/Makefile.in
to add a new source file. This entails a number of steps.(a) Add
$(srcdir)/evalps1.def
to the end ofDEFSRC
.(b) Add
evalps1.o
to the end ofOFILES
.(c) Add the required dependencies:
Third, add the
builtins/evalps1.def
file itself, this is the code that gets executed when you run theevalps1
command:The bulk of that is the GPL licence (since I modified it from
exit.def
) with a very simple function at the end to get and decodePS1
.Lastly, just build the thing in the top level directory:
The
bash
executable that appears can be renamed topaxsh
, though I doubt it will ever become as prevalent as its ancestor :-)And running it, you can see it in action:
When you put one of the
PSx
variables into the prompt, echoing$PS1
simply gives you the variable, while theevalps1
command evaluates it and outputs the result.Now, granted, making code changes to
bash
to add an internal command may be considered by some to be overkill but, if you want an perfect evaluation ofPS1
, it's certainly an option.为什么不自己处理
$PS1
转义替换呢?诸如此类的一系列替换:顺便说一句,zsh 具有解释提示转义的能力。
或者
Why don't you just process the
$PS1
escape substitutions yourself? A series of substitutions such as these:By the way, zsh has the ability to interpret prompt escapes.
or
我喜欢修复 Bash 以使其变得更好的想法,并且我很欣赏 paxdiablo 的详细答案关于如何修补 Bash。有时间我也去一趟
然而,在不修补 Bash 源代码的情况下,我有一个既可移植又不会重复功能的单行黑客,因为该解决方法仅使用 Bash 及其内置函数。
请注意,
tty
和stdio
发生了一些奇怪的事情,因为这也有效:所以尽管我不明白
stdio< 发生了什么/code> 这里,我的 hack 正在 Bash 4.2、NixOS GNU/Linux 上工作。修补 Bash 源代码绝对是一个更优雅的解决方案,而且现在我正在使用 Nix,这应该非常简单且安全。
I like the idea of fixing Bash to make it better, and I appreciate paxdiablo's verbose answer on how to patch Bash. I'll have a go sometime.
However, without patching Bash source-code, I have a one-liner hack that is both portable and doesn't duplicate functionality, because the workaround uses only Bash and its builtins.
Note that there's something strange going on with
tty
's andstdio
seeing as this also works:So although I don't understand what's going on with the
stdio
here, my hack is working for me on Bash 4.2, NixOS GNU/Linux. Patching the Bash source-code is definitely a more elegant solution, and it should be pretty easy and safe to do now that I'm using Nix.两个答案:“纯 bash”和“bash + sed”
简介
当然,从 bash,正确地 gniourf_gniourf 已回答,您必须使用参数转换:
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\001\E]0;user@host: ~\a\002user@host:~$ '请参阅
man -Pless\ +/parameter\ \\ 转换 bash
但对于较旧的 bash,甚至只是为了玩字符串 和变量...
bash 提示扩展,使用
bash
+sed
有我的黑客:
解释:
运行
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
可能会返回一些内容喜欢:
然后,
sed
命令会将:a;$!{N;ba};
),然后end-of-lineexit
by
。 (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
)。<所有内容,以行尾结束>
变为\1
变为\2
。测试用例:
从那里开始,您处于一种伪交互式 shell 中(没有 readline 设施,但这并不重要)...
(最后一行以绿色打印
ubuntu
,@
、:
和$
为黑色,路径 (/tmp
) 为蓝色)纯 bash
简单快捷:
然后现在
或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\001\E]0;user@host: ~\a\002user@host:~$ '请参阅
man -Pless\ +/parameter\ \\ 转换 bash
但对于较旧的 bash,甚至只是为了玩字符串 和变量...
bash 提示扩展,使用
bash
+sed
有我的黑客:
解释:
运行
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
可能会返回一些内容喜欢:
然后,
sed
命令会将:a;$!{N;ba};
),然后end-of-lineexit
by
。 (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
)。<所有内容,以行尾结束>
变为\1
变为\2
。测试用例:
从那里开始,您处于一种伪交互式 shell 中(没有 readline 设施,但这并不重要)...
(最后一行以绿色打印
ubuntu
,@
、:
和$
为黑色,路径 (/tmp
) 为蓝色)纯 bash
简单快捷:
然后现在
或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
Test string\r\n Sat Jan 9 19:23:47 CET 2021\r\n \E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n'或者
请注意,您可以添加进行一些测试以确保字符串正确:
\001\E]0;user@host: ~\a\002user@host:~$ '请参阅
man -Pless\ +/parameter\ \\ 转换 bash
但对于较旧的 bash,甚至只是为了玩字符串 和变量...
bash 提示扩展,使用
bash
+sed
有我的黑客:
解释:
运行
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
可能会返回一些内容喜欢:
然后,
sed
命令会将:a;$!{N;ba};
),然后end-of-lineexit
by
。 (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
)。<所有内容,以行尾结束>
变为\1
变为\2
。测试用例:
从那里开始,您处于一种伪交互式 shell 中(没有 readline 设施,但这并不重要)...
(最后一行以绿色打印
ubuntu
,@
、:
和$
为黑色,路径 (/tmp
) 为蓝色)纯 bash
简单快捷:
然后现在
或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\001\E]0;user@host: ~\a\002user@host:~$ '请参阅
man -Pless\ +/parameter\ \\ 转换 bash
但对于较旧的 bash,甚至只是为了玩字符串 和变量...
bash 提示扩展,使用
bash
+sed
有我的黑客:
解释:
运行
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
可能会返回一些内容喜欢:
然后,
sed
命令会将:a;$!{N;ba};
),然后end-of-lineexit
by
。 (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
)。<所有内容,以行尾结束>
变为\1
变为\2
。测试用例:
从那里开始,您处于一种伪交互式 shell 中(没有 readline 设施,但这并不重要)...
(最后一行以绿色打印
ubuntu
,@
、:
和$
为黑色,路径 (/tmp
) 为蓝色)纯 bash
简单快捷:
然后现在
或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n'使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\001\E]0;user@host: ~\a\002user@host:~$ '请参阅
man -Pless\ +/parameter\ \\ 转换 bash
但对于较旧的 bash,甚至只是为了玩字符串 和变量...
bash 提示扩展,使用
bash
+sed
有我的黑客:
解释:
运行
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
可能会返回一些内容喜欢:
然后,
sed
命令会将:a;$!{N;ba};
),然后end-of-lineexit
by
。 (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
)。<所有内容,以行尾结束>
变为\1
变为\2
。测试用例:
从那里开始,您处于一种伪交互式 shell 中(没有 readline 设施,但这并不重要)...
(最后一行以绿色打印
ubuntu
,@
、:
和$
为黑色,路径 (/tmp
) 为蓝色)纯 bash
简单快捷:
然后现在
或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
\001\E]0;user@host: ~\a\002user@host:~$ '请参阅
man -Pless\ +/parameter\ \\ 转换 bash
但对于较旧的 bash,甚至只是为了玩字符串 和变量...
bash 提示扩展,使用
bash
+sed
有我的黑客:
解释:
运行
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
可能会返回一些内容喜欢:
然后,
sed
命令会将:a;$!{N;ba};
),然后end-of-lineexit
by
。 (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
)。<所有内容,以行尾结束>
变为\1
变为\2
。测试用例:
从那里开始,您处于一种伪交互式 shell 中(没有 readline 设施,但这并不重要)...
(最后一行以绿色打印
ubuntu
,@
、:
和$
为黑色,路径 (/tmp
) 为蓝色)纯 bash
简单快捷:
然后现在
或
使用多行提示进行快速测试:
或者
请注意,您可以添加进行一些测试以确保字符串正确:
Two answer: "Pure bash" and "bash + sed"
Intro
Of course, from version 4.4 of bash, as gniourf_gniourf correctly answered, you have to use parameter transformation:
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\001\E]0;user@host: ~\a\002user@host:~$ 'See
man -Pless\ +/parameter\\\ transformation bash
But for older bash, or even just for playing with strings and variables...
bash prompt expansion, using
bash
+sed
There is my hack:
Explanation:
Running
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
May return something like:
The
sed
command will then:a;$!{N;ba};
), then<everything, terminated by end-of-line><prompt>end-of-line<prompt>exit
by<prompt>
. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
).<everything, terminated by end-of-line>
become\1
<prompt>
become\2
.Test case:
From there, you're in a kind of pseudo interactive shell (without readline facilities, but that's does not matter)...
(Last line print both
ubuntu
in green,@
,:
and$
in black and path (/tmp
) in blue)Pure bash
Simple and quick:
Then now
or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\001\E]0;user@host: ~\a\002user@host:~$ 'See
man -Pless\ +/parameter\\\ transformation bash
But for older bash, or even just for playing with strings and variables...
bash prompt expansion, using
bash
+sed
There is my hack:
Explanation:
Running
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
May return something like:
The
sed
command will then:a;$!{N;ba};
), then<everything, terminated by end-of-line><prompt>end-of-line<prompt>exit
by<prompt>
. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
).<everything, terminated by end-of-line>
become\1
<prompt>
become\2
.Test case:
From there, you're in a kind of pseudo interactive shell (without readline facilities, but that's does not matter)...
(Last line print both
ubuntu
in green,@
,:
and$
in black and path (/tmp
) in blue)Pure bash
Simple and quick:
Then now
or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
Test string\r\n Sat Jan 9 19:23:47 CET 2021\r\n \E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n'Or
Note you could add a little test to ensure string is correct:
\001\E]0;user@host: ~\a\002user@host:~$ 'See
man -Pless\ +/parameter\\\ transformation bash
But for older bash, or even just for playing with strings and variables...
bash prompt expansion, using
bash
+sed
There is my hack:
Explanation:
Running
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
May return something like:
The
sed
command will then:a;$!{N;ba};
), then<everything, terminated by end-of-line><prompt>end-of-line<prompt>exit
by<prompt>
. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
).<everything, terminated by end-of-line>
become\1
<prompt>
become\2
.Test case:
From there, you're in a kind of pseudo interactive shell (without readline facilities, but that's does not matter)...
(Last line print both
ubuntu
in green,@
,:
and$
in black and path (/tmp
) in blue)Pure bash
Simple and quick:
Then now
or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\001\E]0;user@host: ~\a\002user@host:~$ 'See
man -Pless\ +/parameter\\\ transformation bash
But for older bash, or even just for playing with strings and variables...
bash prompt expansion, using
bash
+sed
There is my hack:
Explanation:
Running
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
May return something like:
The
sed
command will then:a;$!{N;ba};
), then<everything, terminated by end-of-line><prompt>end-of-line<prompt>exit
by<prompt>
. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
).<everything, terminated by end-of-line>
become\1
<prompt>
become\2
.Test case:
From there, you're in a kind of pseudo interactive shell (without readline facilities, but that's does not matter)...
(Last line print both
ubuntu
in green,@
,:
and$
in black and path (/tmp
) in blue)Pure bash
Simple and quick:
Then now
or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n'Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\001\E]0;user@host: ~\a\002user@host:~$ 'See
man -Pless\ +/parameter\\\ transformation bash
But for older bash, or even just for playing with strings and variables...
bash prompt expansion, using
bash
+sed
There is my hack:
Explanation:
Running
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
May return something like:
The
sed
command will then:a;$!{N;ba};
), then<everything, terminated by end-of-line><prompt>end-of-line<prompt>exit
by<prompt>
. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
).<everything, terminated by end-of-line>
become\1
<prompt>
become\2
.Test case:
From there, you're in a kind of pseudo interactive shell (without readline facilities, but that's does not matter)...
(Last line print both
ubuntu
in green,@
,:
and$
in black and path (/tmp
) in blue)Pure bash
Simple and quick:
Then now
or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
\001\E]0;user@host: ~\a\002user@host:~$ 'See
man -Pless\ +/parameter\\\ transformation bash
But for older bash, or even just for playing with strings and variables...
bash prompt expansion, using
bash
+sed
There is my hack:
Explanation:
Running
bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
May return something like:
The
sed
command will then:a;$!{N;ba};
), then<everything, terminated by end-of-line><prompt>end-of-line<prompt>exit
by<prompt>
. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/
).<everything, terminated by end-of-line>
become\1
<prompt>
become\2
.Test case:
From there, you're in a kind of pseudo interactive shell (without readline facilities, but that's does not matter)...
(Last line print both
ubuntu
in green,@
,:
and$
in black and path (/tmp
) in blue)Pure bash
Simple and quick:
Then now
or
Quick test with multiline prompts:
Or
Note you could add a little test to ensure string is correct:
另一种可能性:无需编辑 bash 源代码,使用
script
实用程序(ubuntu 上bsdutils
包的一部分):script
命令生成指定的文件 & ;输出也显示在标准输出上。如果省略文件名,它会生成一个名为 typescript 的文件。由于我们对本例中的日志文件不感兴趣,因此文件名指定为
/dev/null
。相反,脚本命令的标准输出将传递给 awk 进行进一步处理。PROMPT_COMMAND
...One more possibility: without editing bash source code, using
script
utility (part ofbsdutils
package on ubuntu):script
command generates a file specified & the output is also shown on stdout. If filename is omitted, it generates a file called typescript.Since we are not interested in the log file in this case, filename is specified as
/dev/null
. Instead the stdout of the script command is passed to awk for further processing.PROMPT_COMMAND
...ps=${ps@P}
展开它 (bash 4.4)\x01
和\x02
之间删除(由 bash 创建,替换 < code>\[ 和\]
占位符。应输出如下内容:
如果存在某些控制字符,您可以按照此 stackoverflow:从文本流中删除 ANSI 颜色代码。
我使用以下命令删除 github: mouse_xterm 中的 SCI 和 OSC。
ps=${ps@P}
(bash 4.4)\x01
and\x02
(created by bash replacing the\[
and\]
placeholder.Should output something like this:
If some control characters are present, you can remove it as stated in this stackoverflow: Removing ANSI color codes from text stream.
I used the following to remove SCI and OSC in github: mouse_xterm.