bash Echo 扩展 PS1
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3451993/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Echo expanded PS1
提问by l0b0
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
我有一个 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)')$
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 $PS1only evaluates the colors.
我想回应结果提示username@hostname:/path$,最好(但不一定)用漂亮的颜色。粗略地看一下 Bash 手册并没有发现任何明确的答案,echo -e $PS1只是评估了颜色。
采纳答案by gniourf_gniourf
Since Bash 4.4 you can use the @Pexpansion:
从 Bash 4.4 开始,您可以使用@P扩展:
First I put your prompt string in a variable mypromptusing read -rand a quoted here-doc:
首先,我将您的提示字符串放入变量mypromptusingread -r和引用的 here-doc 中:
read -r myprompt <<'EOF'
${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)')$
EOF
To print the prompt (as it would be interpreted if it were PS1), use the expansion ${myprompt@P}:
要打印提示(如果是的话会被解释PS1),请使用扩展${myprompt@P}:
$ printf '%s\n' "${myprompt@P}"
gniourf@rainbow:~$
$
(In fact there are some \001and \002characters, 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).
(事实上,有一些\001和\002人物,从未来\[和\]你不能看到这里,但你可以看到他们,如果你尝试编辑这个职位,你还会看到他们在你的终端,如果你输入的命令)。
To get rid of these, the trick sent by Dennis Williamson on the bash mailing list is to use read -e -pso that these characters get interpreted by the readline library:
为了摆脱这些,丹尼斯·威廉姆森在 bash 邮件列表上发送的技巧是使用read -e -p这些字符,以便 readline 库解释这些字符:
read -e -p "${myprompt@P}"
This will prompt the user, with the mypromptcorrectly interpreted.
这将提示用户,并myprompt正确解释。
To this post, Greg Wooledge answered that you might as well just strip the \001and \002from the string. This can be achieved like so:
对于这篇文章,Greg Wooledge 回答说,您不妨从字符串中剥离\001和\002。这可以像这样实现:
myprompt=${myprompt@P}
printf '%s\n' "${myprompt//[$'( set +o emacs +o vi; printf '%s\n' "${myprompt@P}" )
1'$'char *decode_prompt_string (string) char *string; { ... }
2']}"
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:
对于这篇文章,Chet Ramey 回答说您也可以使用set +o emacs +o vi. 所以这也可以:
echo "#define DISTVERSION \"${float_dist}-pax\""
回答by paxdiablo
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 \uwith $USERand so on). However, this requires a lot of duplication of functionality and ensuring that the code is kept in sync with whatever bashdoes in future.
Bash 本身不提供此功能,但您可以使用各种技巧来提供子集(例如替换\u为$USER等等)。然而,这需要大量的功能重复,并确保代码与bash未来的任何内容保持同步。
If you want to get allthe 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 a y.tab.cfile which contains the decode_prompt_string()function:
如果您下载bash(我正在查看 4.2 版)的代码,则会有一个y.tab.c包含该decode_prompt_string()函数的文件:
evalps1.o: evalps1.def $(topdir)/bashtypes.h $(topdir)/config.h \
$(topdir)/bashintl.h $(topdir)/shell.h common.h
This is the function that evaluates the PSxvariables for prompting. In order to allow this functionality to be provided to users of the shell itself (rather than just used bythe shell), you can follow these steps to add an internal command evalps1.
这是评估PSx变量以进行提示的函数。为了允许将此功能提供给 shell 本身的用户(而不是仅由shell 使用),您可以按照以下步骤添加内部命令evalps1。
First, change support/mkversion.shso 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 -paxbit):
首先,进行更改,support/mkversion.sh以免将其与“真实”混淆bash,并且 FSF 可以出于保修目的拒绝所有知识:-) 只需更改一行(我添加了该-pax位):
This file is evalps1.def, from which is created evalps1.c.
It implements the builtin "evalps1" in Bash.
Copyright (C) 1987-2009 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
$PRODUCES evalps1.c
$BUILTIN evalps1
$FUNCTION evalps1_builtin
$SHORT_DOC evalps1
Outputs the fully interpreted PS1 prompt.
Outputs the PS1 prompt, fully evaluated, for whatever nefarious purposes
you require.
$END
#include <config.h>
#include "../bashtypes.h"
#include <stdio.h>
#include "../bashintl.h"
#include "../shell.h"
#include "common.h"
int
evalps1_builtin (list)
WORD_LIST *list;
{
char *ps1 = get_string_value ("PS1");
if (ps1 != 0)
{
ps1 = decode_prompt_string (ps1);
if (ps1 != 0)
{
printf ("%s", ps1);
}
}
return 0;
}
Second, change builtins/Makefile.into add a new source file. This entails a number of steps.
其次,更改builtins/Makefile.in以添加新的源文件。这需要许多步骤。
(a) Add $(srcdir)/evalps1.defto the end of DEFSRC.
(a) 添加$(srcdir)/evalps1.def到DEFSRC.
(b) Add evalps1.oto the end of OFILES.
(b) 添加evalps1.o到OFILES.
(c) Add the required dependencies:
(c) 添加所需的依赖项:
./configure
make
Third, add the builtins/evalps1.deffile itself, this is the code that gets executed when you run the evalps1command:
第三,添加builtins/evalps1.def文件本身,这是运行evalps1命令时执行的代码:
pax> mv bash paxsh
pax> ./paxsh --version
GNU bash, version 4.2-pax.0(1)-release (i686-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
pax> ./paxsh
pax> echo $BASH_VERSION
4.2-pax.0(1)-release
pax> echo "[$PS1]"
[pax> ]
pax> echo "[$(evalps1)]"
[pax> ]
pax> PS1="\h: "
paxbox01: echo "[$PS1]"
[\h: ]
paxbox01: echo "[$(evalps1)]"
[paxbox01: ]
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 decode PS1.
其中大部分是 GPL 许可证(因为我修改了它exit.def),最后有一个非常简单的功能来获取和解码PS1.
Lastly, just build the thing in the top level directory:
最后,只需在顶级目录中构建事物:
p="${PS1//\u/$USER}"; p="${p//\h/$HOSTNAME}"
The bashexecutable that appears can be renamed to paxsh, though I doubt it will ever become as prevalent as its ancestor :-)
bash出现的可执行文件可以重命名为paxsh,尽管我怀疑它是否会像它的祖先一样流行:-)
And running it, you can see it in action:
运行它,你可以看到它在运行:
print -P '%n@%m %d'
When you put one of the PSxvariables into the prompt, echoing $PS1simply gives you the variable, while the evalps1command evaluates it and outputs the result.
当您将其中一个PSx变量放入提示中时,回显$PS1只是为您提供变量,而evalps1命令会对其进行评估并输出结果。
Now, granted, making code changes to bashto add an internal command may be considered by some to be overkill but, if you want an perfect evaluation of PS1, it's certainly an option.
现在,当然,更改代码bash以添加内部命令可能会被某些人认为是矫枉过正,但是,如果您想要对 的完美评估PS1,这当然是一个选择。
回答by Paused until further notice.
Why don't you just process the $PS1escape substitutions yourself? A series of substitutions such as these:
为什么不自己处理$PS1转义替换?一系列替换,例如:
p=${(%%)PS1}
By the way, zsh has the ability to interpret prompt escapes.
顺便说一下,zsh 具有解释提示转义的能力。
x="$(PS1=\"$PS1\" echo -n | bash --norc -i 2>&1)"; echo "'${x%exit}'"
or
或者
x="$(PS1=\"$PS1\" echo -n | bash --norc -i 2>&1 > /dev/null)"; echo "'${x%exit}'"
回答by James Haigh
I like the idea of fixing Bash to make it better, and I appreciate paxdiablo's verbose answeron how to patch Bash. I'll have a go sometime.
我喜欢修复 Bash 以使其更好的想法,我很欣赏 paxdiablo关于如何修补 Bash的详细回答。有时间我去看看
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.
但是,在不修补 Bash 源代码的情况下,我有一个既可移植又不重复功能的单行 hack,因为该解决方法仅使用 Bash 及其内置程序。
ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 |
sed ':;$!{N;b};s/^\(.*\n\)*\(.*\)\nexit$//p;d')"
Note that there's something strange going on with tty's and stdioseeing as this also works:
请注意,tty's发生了一些奇怪的事情,stdio因为这也有效:
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
ubuntu@ubuntu:~$
ubuntu@ubuntu:~$ exit
So although I don't understand what's going on with the stdiohere, 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.
因此,虽然我不明白stdio这里发生了什么,但我的 hack 在 Bash 4.2、NixOS GNU/Linux 上对我有用。修补 Bash 源代码绝对是一个更优雅的解决方案,现在我使用 Nix 应该很容易和安全。
回答by F. Hauri
Two answer: "Pure bash" and "bash + sed"
两个答案:“纯bash”和“bash + sed”
As doing this by using sedis simplier, the first answer will use sed.
由于使用sed更简单,第一个答案将使用sed。
See below for pure bashsolution.
请参阅下面的纯bash解决方案。
bashprompt expansion, bash+ sed
bash提示扩展,bash+sed
There is my hack:
这是我的黑客:
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
ubuntu@ubuntu:~$
ubuntu@ubuntu:~$ exit
Explanation:
解释:
Running bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
跑步 bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1
May return something like:
可能会返回类似的东西:
while ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 | sed ':;$!{N;b};s/^\(.*\n\)*\(.*\)\nexit$//p;d')" read -rp "$ExpPS1" && [ "$REPLY" != exit ] ;do eval "$REPLY" done
ubuntu@ubuntu:~$ cd /tmp ubuntu@ubuntu:/tmp$ PS1="${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$ " ubuntu@ubuntu:/tmp$
The sedcommand will then
然后sed命令将
- take all lines into one buffer (
:;$!{N;b};), than - replace
<everything, terminated by end-of-line><prompt>end-of-line<prompt>exitby<prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).- where
<everything, terminated by end-of-line>become\1 - and
<prompt>become\2.
- where
- 将所有行放入一个缓冲区 (
:;$!{N;b};),然后 - 替换
<everything, terminated by end-of-line><prompt>end-of-line<prompt>exit为<prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).- 哪里
<everything, terminated by end-of-line>变成\1 - 并
<prompt>成为\2。
- 哪里
ubuntu@ubuntu:/tmp$ exit
ubuntu@ubuntu:/tmp$ od -A n -t c <<< $ExpPS1
033 [ 1 ; 3 2 m u b u n t u 033 [ 0
m @ 033 [ 1 ; 3 2 m u b u n t u 033
[ 0 m : 033 [ 1 ; 3 4 m ~ 033 [ 0 m
$ \n
From there, you're in a kind of pseudo interactive shell (without readline facilities, but that's does not matter)...
从那里,您处于一种伪交互式 shell(没有 readline 设施,但这并不重要)......
ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1)"
ExpPS1_W="${ExpPS1%exit}"
ExpPS1="${ExpPS1_W##*$'\n'}"
ExpPS1_L=${ExpPS1_W%$'\n'$ExpPS1}
while [ "${ExpPS1_W%$'\n'$ExpPS1}" = "$ExpPS1_W" ] ||
[ "${ExpPS1_L%$'\n'$ExpPS1}" = "$ExpPS1_L" ] ;do
ExpPS1_P="${ExpPS1_L##*$'\n'}"
ExpPS1_L=${ExpPS1_L%$'\n'$ExpPS1_P}
ExpPS1="$ExpPS1_P"$'\n'"$ExpPS1"
done
(Last line print both ubuntuin green, @, :and $in black and path (/tmp) in blue)
(最后输出都ubuntu在绿色@,:并$有黑色和路径(/tmp)蓝色)
ExpPS1="$(bash --rcfile <(echo "PS1='${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$ '") -i <<<'' 2>&1)"
Pure bash
纯粹的bash
ExpPS1="$(bash --rcfile <(echo "PS1='Test string\n$(date)\n$PS1'") -i <<<'' 2>&1)";
The whileloop is required to ensure correct handling of multiline prompts:
在while确保多提示正确处理循环是必需的:
replace 1st line by:
将第一行替换为:
echo "$ExpPS1"
Test string
Tue May 10 11:04:54 UTC 2016
ubuntu@ubuntu:~$
od -A n -t c <<<${ExpPS1}
T e s t s t r i n g \r T u e
M a y 1 0 1 1 : 0 4 : 5 4
U T C 2 0 1 6 \r 033 ] 0 ; u
b u n t u @ u b u n t u : ~ \a
u b u n t u @ u b u n t u : ~ $
\n
or
或者
$ TEST_PS1="\e[31;1m\u@\h:\n\e[0;1m$ \e[0m"
$ RANDOM_STRING=some_random_string_here_that_is_not_part_of_PS1
$ script /dev/null <<-EOF | awk 'NR==2' RS=$RANDOM_STRING
PS1="$TEST_PS1"; HISTFILE=/dev/null
echo -n $RANDOM_STRING
echo -n $RANDOM_STRING
exit
EOF
<prints the prompt properly here>
The last multilinewill print:
最后多行将打印:
##代码##回答by tomlogic
You may have to write a small C program that uses the same code bash does (is it a library call?) to display that prompt, and just call the C program. Granted, that's not very portable since you'll have to compile it on each platform, but it's a possible solution.
您可能需要编写一个小 C 程序,它使用与 bash 相同的代码(它是库调用吗?)来显示该提示,然后只调用 C 程序。当然,这不是很便携,因为您必须在每个平台上编译它,但这是一个可能的解决方案。
回答by anishsane
One more possibility: without editing bash source code, using scriptutility (part of bsdutilspackage on ubuntu):
另一种可能性:不编辑 bash 源代码,使用script实用程序(bsdutilsubuntu 上的包的一部分):
scriptcommand generates a file specified & the output is also shown on stdout. If filename is omitted, it generates a file called typescript.
script命令生成指定的文件,输出也显示在标准输出上。如果省略 filename,它会生成一个名为 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.
由于在这种情况下我们对日志文件不感兴趣,因此将文件名指定为/dev/null. 而是将脚本命令的标准输出传递给 awk 进行进一步处理。
- The entire code can also be encapsulated into a function.
- Also, the output prompt can also be assigned to a variable.
- This approach also supports parsing of
PROMPT_COMMAND...
- 整个代码也可以封装成一个函数。
- 此外,输出提示也可以分配给变量。
- 这种方法还支持解析
PROMPT_COMMAND...

