如何扩展PS1?

发布于 2024-09-14 09:51:45 字数 551 浏览 6 评论 0原文

我有一个 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 技术交流群。

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

发布评论

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

评论(7

舞袖。长 2024-09-21 09:51:45

从 Bash 4.4 开始,您可以使用 @P 扩展:

首先,我使用 read -r 将提示字符串放入变量 myprompt 中,并在此处引用-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

要打印提示(因为如果是 PS1 则会被解释),请使用扩展 ${myprompt@P}:(

$ printf '%s\n' "${myprompt@P}"
gniourf@rainbow:~$
$

事实上有一些< code>\001 和 \002 字符,来自 \[\],您在此处看不到,但如果您尝试编辑这篇文章,您就可以看到它们;如果您键入命令,您也会在终端中看到它们)。


为了摆脱这些,Dennis Williamson 在 bash 邮件列表上发送的技巧是使用 read -e -p 以便这些字符由 readline 库解释:

read -e -p "${myprompt@P}"

这将提示用户, myprompt 正确解释。

对于这篇文章,Greg Wooledge 回答说,您不妨从字符串中删除 \001\002 。这可以通过如下方式实现:

myprompt=${myprompt@P}
printf '%s\n' "${myprompt//[

在这篇文章中,Chet Ramey 回答说,您也可以使用 set +o emacs +o vi 完全关闭行编辑。所以这也可以:

( set +o emacs +o vi; printf '%s\n' "${myprompt@P}" )
\001'

在这篇文章中,Chet Ramey 回答说,您也可以使用 set +o emacs +o vi 完全关闭行编辑。所以这也可以:


\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 using read -r and a quoted 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}:

$ printf '%s\n' "${myprompt@P}"
gniourf@rainbow:~$
$

(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:

read -e -p "${myprompt@P}"

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:

myprompt=${myprompt@P}
printf '%s\n' "${myprompt//[

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:

( set +o emacs +o vi; printf '%s\n' "${myprompt@P}" )
\001'

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:


\002']}"

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:

不必你懂 2024-09-21 09:51:45

开源软件的一大优点是源代码是开放的:-)

Bash 本身不提供此功能,但您可以使用各种技巧来提供子集(例如替换 \u$USER 等)。然而,这需要大量重复的功能,并确保代码与 bash 将来所做的任何事情保持同步。

如果您想获得提示变量的全部功能(并且您不介意编写一些代码(如果您介意的话,您为什么在这里?)),添加到 shell 本身很容易。

如果您下载 bash 的代码(我正在查看版本 4.2),则会有一个 y.tab.c 文件,其中包含 decode_prompt_string() 函数:

char *decode_prompt_string (string) char *string; { ... }

这是评估 PSx 变量以进行提示的函数。为了允许向 shell 本身的用户提供此功能(而不是仅由 shell 使用),您可以按照以下步骤添加内部命令 evalps1

首先,更改 support/mkversion.sh,这样您就不会将其与“真正的”bash 混淆,并且 FSF 可以出于保修目的拒绝所有知识: -) 只需更改一行(我添加了 -pax 位):

echo "#define DISTVERSION \"${float_dist}-pax\""

其次,更改 builtins/Makefile.in 以添加新的源文件。这需要执行多个步骤。

(a) 将 $(srcdir)/evalps1.def 添加到 DEFSRC 的末尾。

(b) 将 evalps1.o 添加到 OFILES 的末尾。

(c) 添加所需的依赖项:

evalps1.o: evalps1.def $(topdir)/bashtypes.h $(topdir)/config.h \
           $(topdir)/bashintl.h $(topdir)/shell.h common.h

第三,添加 builtins/evalps1.def 文件本身,这是运行 evalps1 命令时执行的代码

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;
}

:其中包括 GPL 许可证(因为我从 exit.def 修改了它),最后有一个非常简单的函数来获取和解码 PS1

最后,只需在顶级目录中构建该东西:

./configure
make

出现的 bash 可执行文件可以重命名为 paxsh ,尽管我怀疑它会像它的祖先一样流行: -)

运行它,您可以看到它的实际效果:

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: ]

当您将 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 whatever bash 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 a y.tab.c file which contains the decode_prompt_string() function:

char *decode_prompt_string (string) char *string; { ... }

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 command evalps1.

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):

echo "#define DISTVERSION \"${float_dist}-pax\""

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 of DEFSRC.

(b) Add evalps1.o to the end of OFILES.

(c) Add the required dependencies:

evalps1.o: evalps1.def $(topdir)/bashtypes.h $(topdir)/config.h \
           $(topdir)/bashintl.h $(topdir)/shell.h common.h

Third, add the builtins/evalps1.def file itself, this is the code that gets executed when you run the evalps1 command:

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;
}

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.

Lastly, just build the thing in the top level directory:

./configure
make

The bash executable that appears can be renamed to paxsh, though I doubt it will ever become as prevalent as its ancestor :-)

And running it, you can see it in action:

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: ]

When you put one of the PSx variables into the prompt, echoing $PS1 simply gives you the variable, while the evalps1 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 of PS1, it's certainly an option.

风透绣罗衣 2024-09-21 09:51:45

为什么不自己处理 $PS1 转义替换呢?诸如此类的一系列替换:

p="${PS1//\\u/$USER}"; p="${p//\\h/$HOSTNAME}"

顺便说一句,zsh 具有解释提示转义的能力。

print -P '%n@%m %d'

或者

p=${(%%)PS1}

Why don't you just process the $PS1 escape substitutions yourself? A series of substitutions such as these:

p="${PS1//\\u/$USER}"; p="${p//\\h/$HOSTNAME}"

By the way, zsh has the ability to interpret prompt escapes.

print -P '%n@%m %d'

or

p=${(%%)PS1}
眼藏柔 2024-09-21 09:51:45

我喜欢修复 Bash 以使其变得更好的想法,并且我很欣赏 paxdiablo 的详细答案关于如何修补 Bash。有时间我也去一趟

然而,在不修补 Bash 源代码的情况下,我有一个既可移植又不会重复功能的单行黑客,因为该解决方法仅使用 Bash 及其内置函数。

x="$(PS1=\"$PS1\" echo -n | bash --norc -i 2>&1)"; echo "'${x%exit}'"

请注意,ttystdio 发生了一些奇怪的事情,因为这也有效:

x="$(PS1=\"$PS1\" echo -n | bash --norc -i 2>&1 > /dev/null)"; echo "'${x%exit}'"

所以尽管我不明白 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.

x="$(PS1=\"$PS1\" echo -n | bash --norc -i 2>&1)"; echo "'${x%exit}'"

Note that there's something strange going on with tty's and stdio seeing as this also works:

x="$(PS1=\"$PS1\" echo -n | bash --norc -i 2>&1 > /dev/null)"; echo "'${x%exit}'"

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.

仅冇旳回忆 2024-09-21 09:51:45

两个答案:“纯 bash”和“bash + sed”

简介

当然,从 ,正确地 gniourf_gniourf 已回答,您必须使用参数转换

ExpPS1=${PS1@P}
echo ${ExpPS1@Q}

请参阅man -Pless\ +/parameter\ \\ 转换 bash

但对于较旧的 bash,甚至只是为了玩字符串变量...

提示扩展,使用 bash + sed

有我的黑客:

ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 |
              sed ':a;$!{N;ba};s/^\(.*\n\)*\(.*\)\n\2exit$/\2/p;d')"

解释:

运行 bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1

可能会返回一些内容喜欢:

用户@主机:~$ 
用户@主机:~$ 退出

然后,sed 命令会将

  • 所有行放入一个缓冲区 (:a;$!{N;ba};),然后
  • 替换 end-of-lineexit by 。 (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/)。
    • 其中 <所有内容,以行尾结束> 变为 \1
    • 变为 \2

测试用例:

while ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 |
          sed ':a;$!{N;ba};s/^\(.*\n\)*\(.*\)\n\2exit$/\2/p;d')"
    read -rp "$ExpPS1" && [ "$REPLY" != exit ] ;do
    eval "$REPLY"
  done

从那里开始,您处于一种伪交互式 shell 中(没有 readline 设施,但这并不重要)...

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$ 

(最后一行以绿色打印 ubuntu@ :$ 为黑色,路径 (/tmp) 为蓝色)

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

简单快捷:

ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1)"
mapfile ExpPS1 <<<"${ExpPS1%exit}"
ExpPS1=( "${ExpPS1[*]::${#ExpPS1[@]}/2}" )

然后现在

declare -p ExpPS1
declare -a ExpPS1=([0]=

echo ${ExpPS1@Q}

使用多行提示进行快速测试:

ExpPS1="$(bash --rcfile <(echo "PS1='Test string\n$(date)\n$PS1'"
    ) -i <<<'' 2>&1)";
mapfile ExpPS1 <<<"${ExpPS1%exit}"
ExpPS1=( "${ExpPS1[*]::${#ExpPS1[@]}/2}" )    

echo ${ExpPS1@Q}

或者

od -A n -t c  <<<${ExpPS1}
   T   e   s   t       s   t   r   i   n   g  \r  \n       S   a
   t       J   a   n           9       1   9   :   2   6   :   3
   9       C   E   T       2   0   2   1  \r  \n     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  \n

请注意,您可以添加进行一些测试以确保字符串正确:

ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1)"
mapfile ExpPS1 <<<"${ExpPS1%exit}"
[ "${ExpPS1[*]::${#ExpPS1[@]}/2}" = "${ExpPS1[*]: -${#ExpPS1[@]}/2}" ] ||
    echo WARNING: First half seem not match last half string.
ExpPS1=( "${ExpPS1[*]::${#ExpPS1[@]}/2}" )
\001\E]0;user@host: ~\a\002user@host:~$ '

请参阅man -Pless\ +/parameter\ \\ 转换 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) 为蓝色)


简单快捷:


然后现在



使用多行提示进行快速测试:


或者


请注意,您可以添加进行一些测试以确保字符串正确:


\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')


使用多行提示进行快速测试:


或者


请注意,您可以添加进行一些测试以确保字符串正确:


\001\E]0;user@host: ~\a\002user@host:~$ '

请参阅man -Pless\ +/parameter\ \\ 转换 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) 为蓝色)


简单快捷:


然后现在



使用多行提示进行快速测试:


或者


请注意,您可以添加进行一些测试以确保字符串正确:


\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n'

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\001\E]0;user@host: ~\a\002user@host:~$ '

请参阅man -Pless\ +/parameter\ \\ 转换 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) 为蓝色)

简单快捷:

然后现在

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\001\E]0;user@host: ~\a\002user@host:~$ '

请参阅man -Pless\ +/parameter\ \\ 转换 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) 为蓝色)

简单快捷:

然后现在

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

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 + 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) 为蓝色)

简单快捷:

然后现在

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\001\E]0;user@host: ~\a\002user@host:~$ '

请参阅man -Pless\ +/parameter\ \\ 转换 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) 为蓝色)

简单快捷:

然后现在

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n'

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\001\E]0;user@host: ~\a\002user@host:~$ '

请参阅man -Pless\ +/parameter\ \\ 转换 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) 为蓝色)

简单快捷:

然后现在

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\E]0;ubuntu@ubuntu: ~\aubuntu@ubuntu:~$ \n')

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

\001\E]0;user@host: ~\a\002user@host:~$ '

请参阅man -Pless\ +/parameter\ \\ 转换 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) 为蓝色)

简单快捷:

然后现在

使用多行提示进行快速测试:

或者

请注意,您可以添加进行一些测试以确保字符串正确:

Two answer: "Pure bash" and "bash + sed"

Intro

Of course, from version 4.4 of , as gniourf_gniourf correctly answered, you have to use parameter transformation:

ExpPS1=${PS1@P}
echo ${ExpPS1@Q}

See man -Pless\ +/parameter\\\ transformation bash

But for older bash, or even just for playing with strings and variables...

prompt expansion, using bash + sed

There is my hack:

ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 |
              sed ':a;$!{N;ba};s/^\(.*\n\)*\(.*\)\n\2exit$/\2/p;d')"

Explanation:

Running bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1

May return something like:

user@host:~$ 
user@host:~$ exit

The sed command will then

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <prompt> become \2.

Test case:

while ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1 |
          sed ':a;$!{N;ba};s/^\(.*\n\)*\(.*\)\n\2exit$/\2/p;d')"
    read -rp "$ExpPS1" && [ "$REPLY" != exit ] ;do
    eval "$REPLY"
  done

From there, you're in a kind of pseudo interactive shell (without readline facilities, but that's does not matter)...

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$ 

(Last line print both ubuntu in green, @, : and $ in black and path (/tmp) in blue)

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

Pure

Simple and quick:

ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1)"
mapfile ExpPS1 <<<"${ExpPS1%exit}"
ExpPS1=( "${ExpPS1[*]::${#ExpPS1[@]}/2}" )

Then now

declare -p ExpPS1
declare -a ExpPS1=([0]=

or

echo ${ExpPS1@Q}

Quick test with multiline prompts:

ExpPS1="$(bash --rcfile <(echo "PS1='Test string\n$(date)\n$PS1'"
    ) -i <<<'' 2>&1)";
mapfile ExpPS1 <<<"${ExpPS1%exit}"
ExpPS1=( "${ExpPS1[*]::${#ExpPS1[@]}/2}" )    

echo ${ExpPS1@Q}

Or

od -A n -t c  <<<${ExpPS1}
   T   e   s   t       s   t   r   i   n   g  \r  \n       S   a
   t       J   a   n           9       1   9   :   2   6   :   3
   9       C   E   T       2   0   2   1  \r  \n     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  \n

Note you could add a little test to ensure string is correct:

ExpPS1="$(bash --rcfile <(echo "PS1='$PS1'") -i <<<'' 2>&1)"
mapfile ExpPS1 <<<"${ExpPS1%exit}"
[ "${ExpPS1[*]::${#ExpPS1[@]}/2}" = "${ExpPS1[*]: -${#ExpPS1[@]}/2}" ] ||
    echo WARNING: First half seem not match last half string.
ExpPS1=( "${ExpPS1[*]::${#ExpPS1[@]}/2}" )
\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...

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

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <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

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...

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

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <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

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...

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

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <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

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...

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

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <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

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...

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

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <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

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...

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

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <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

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...

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

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <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

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...

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

  • take all lines into one buffer (:a;$!{N;ba};), then
  • replace <everything, terminated by end-of-line><prompt>end-of-line<prompt>exit by <prompt>. (s/^\(.*\n\)*\(.*\)\n\2exit$/\2/).
    • where <everything, terminated by end-of-line> become \1
    • and <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

Simple and quick:

Then now

or

Quick test with multiline prompts:

Or

Note you could add a little test to ensure string is correct:

蓝天 2024-09-21 09:51:45

另一种可能性:无需编辑 bash 源代码,使用 script 实用程序(ubuntu 上 bsdutils 包的一部分):

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

script 命令生成指定的文件 & ;输出也显示在标准输出上。如果省略文件名,它会生成一个名为 typescript 的文件。

由于我们对本例中的日志文件不感兴趣,因此文件名指定为 /dev/null。相反,脚本命令的标准输出将传递给 awk 进行进一步处理。

  1. 整个代码也可以封装成一个函数。
  2. 此外,输出提示也可以分配给变量。
  3. 此方法还支持解析 PROMPT_COMMAND...

One more possibility: without editing bash source code, using script utility (part of bsdutils package on ubuntu):

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

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.

  1. The entire code can also be encapsulated into a function.
  2. Also, the output prompt can also be assigned to a variable.
  3. This approach also supports parsing of PROMPT_COMMAND...
夜雨飘雪 2024-09-21 09:51:45
  1. 使用 ps=${ps@P} 展开它 (bash 4.4)
  2. \x01\x02 之间删除(由 bash 创建,替换 < code>\[ 和 \] 占位符。
  3. 检查剩余的所有字符
ps1_size(){
  # Ref1: https://stackoverflow.com/questions/3451993/how-to-expand-ps1
  >&2 echo -e "\nP0: Raw"
  local ps=$PS1
  echo -n "$ps" | xxd >&2 

  >&2 echo -e "\nP1: Expanding (require bash 4.4)"
  ps=${ps@P}
  echo -n "$ps" | xxd >&2 

  >&2 echo -e "\nP2: Removing everything 01 and 02"
  shopt -s extglob
  ps=${ps//

应输出如下内容:

~/Software/Bash/Mouse (master)$ source ../ps1_size.sh

P0: Raw
00000000: 5c5b 5c65 5d30 3b60 7061 7273 655f 7469  \[\e]0;`parse_ti
00000010: 746c 6560 5c30 3037 5c5d 5c5b 5c65 5b33  tle`\007\]\[\e[3
00000020: 326d 5c5d 5c77 205c 5b5c 655b 3333 6d5c  2m\]\w \[\e[33m\
00000030: 5d60 7061 7273 655f 6769 745f 6272 616e  ]`parse_git_bran
00000040: 6368 605c 5b5c 655b 306d 5c5d 2420       ch`\[\e[0m\]$

P1: Expanding (require bash 4.4)
00000000: 011b 5d30 3b7e 2f53 6f66 7477 6172 652f  ..]0;~/Software/
00000010: 4261 7368 2f4d 6f75 7365 0702 011b 5b33  Bash/Mouse....[3
00000020: 326d 027e 2f53 6f66 7477 6172 652f 4261  2m.~/Software/Ba
00000030: 7368 2f4d 6f75 7365 2001 1b5b 3333 6d02  sh/Mouse ..[33m.
00000040: 286d 6173 7465 7229 011b 5b30 6d02 2420  (master)..[0m.$

P2: Removing everything 01 and 02
00000000: 7e2f 536f 6674 7761 7265 2f42 6173 682f  ~/Software/Bash/
00000010: 4d6f 7573 6520 286d 6173 7465 7229 2420  Mouse (master)$

P3: Checking
32~/Software/Bash/Mouse (master)$

如果存在某些控制字符,您可以按照此 stackoverflow:从文本流中删除 ANSI 颜色代码
我使用以下命令删除 github: mouse_xterm 中的 SCI 和 OSC。

  # Sanitize, in case
  ps=$(LC_ALL=C sed '
    # Safety
    s/\x01\|\x02//g;
    # Safety Remove OSC https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
    # 20 .. 7e => printable characters
    # 07 => BEL
    # 9C => ST
    # 1b 5C => ESC + BS
    s/\x1b\][0-9;]*[\x20-\x7e]*\([\x07\x9C]\|\x1b\\\)//g;
    # Safety: Remove all escape sequences https://superuser.com/questions/380772/removing-ansi-color-codes-from-text-stream
    s/\x1b\[[0-9;]*[a-zA-Z]//g;
  ' <<< "$ps")
\x01'*([^

应输出如下内容:


如果存在某些控制字符,您可以按照此 stackoverflow:从文本流中删除 ANSI 颜色代码
我使用以下命令删除 github: mouse_xterm 中的 SCI 和 OSC。


\x02'])

应输出如下内容:


如果存在某些控制字符,您可以按照此 stackoverflow:从文本流中删除 ANSI 颜色代码
我使用以下命令删除 github: mouse_xterm 中的 SCI 和 OSC。


\x02'}
  echo -n "$ps" | xxd >&2 

  >&2 echo -e "\nP3: Checking"
  if [[ "$ps" =~ [\x07\x1b\x9c] ]]; then
    # Check if escape inside
    # 07 => BEL
    # 1b => ESC
    # 9C => ST
    >&2 echo 'Warning: There is an escape code in your PS1 which is not betwwen \[ \]'
    >&2 echo "Tip: put \[ \] around your escape codes (ctlseqs + associated parameters)"
    echo -n "$ps" | xxd >&2
  # Check printable characters <= 20 .. 7e, and newline
  # -- Remove the trailing 0x0a (BEL)
  elif [[ "$ps" =~ [^[:graph:][:space:]] ]]; then
    >&2 echo 'Warning: There is a non printable character in PS1 which is not between \[ \]'
    >&2 echo "Tip: put \[ \] around your escape codes (ctlseqs + associated parameters)"
    echo "$ps"
    echo -n "$ps" | xxd >&2 
  fi

  # Echo result
  echo -n "${#ps}"
}

ps1_size

应输出如下内容:

如果存在某些控制字符,您可以按照此 stackoverflow:从文本流中删除 ANSI 颜色代码
我使用以下命令删除 github: mouse_xterm 中的 SCI 和 OSC。

  1. Expand it with ps=${ps@P} (bash 4.4)
  2. Remove between \x01 and \x02 (created by bash replacing the \[ and \] placeholder.
  3. Check all characters left
ps1_size(){
  # Ref1: https://stackoverflow.com/questions/3451993/how-to-expand-ps1
  >&2 echo -e "\nP0: Raw"
  local ps=$PS1
  echo -n "$ps" | xxd >&2 

  >&2 echo -e "\nP1: Expanding (require bash 4.4)"
  ps=${ps@P}
  echo -n "$ps" | xxd >&2 

  >&2 echo -e "\nP2: Removing everything 01 and 02"
  shopt -s extglob
  ps=${ps//

Should output something like this:

~/Software/Bash/Mouse (master)$ source ../ps1_size.sh

P0: Raw
00000000: 5c5b 5c65 5d30 3b60 7061 7273 655f 7469  \[\e]0;`parse_ti
00000010: 746c 6560 5c30 3037 5c5d 5c5b 5c65 5b33  tle`\007\]\[\e[3
00000020: 326d 5c5d 5c77 205c 5b5c 655b 3333 6d5c  2m\]\w \[\e[33m\
00000030: 5d60 7061 7273 655f 6769 745f 6272 616e  ]`parse_git_bran
00000040: 6368 605c 5b5c 655b 306d 5c5d 2420       ch`\[\e[0m\]$

P1: Expanding (require bash 4.4)
00000000: 011b 5d30 3b7e 2f53 6f66 7477 6172 652f  ..]0;~/Software/
00000010: 4261 7368 2f4d 6f75 7365 0702 011b 5b33  Bash/Mouse....[3
00000020: 326d 027e 2f53 6f66 7477 6172 652f 4261  2m.~/Software/Ba
00000030: 7368 2f4d 6f75 7365 2001 1b5b 3333 6d02  sh/Mouse ..[33m.
00000040: 286d 6173 7465 7229 011b 5b30 6d02 2420  (master)..[0m.$

P2: Removing everything 01 and 02
00000000: 7e2f 536f 6674 7761 7265 2f42 6173 682f  ~/Software/Bash/
00000010: 4d6f 7573 6520 286d 6173 7465 7229 2420  Mouse (master)$

P3: Checking
32~/Software/Bash/Mouse (master)$

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.

  # Sanitize, in case
  ps=$(LC_ALL=C sed '
    # Safety
    s/\x01\|\x02//g;
    # Safety Remove OSC https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
    # 20 .. 7e => printable characters
    # 07 => BEL
    # 9C => ST
    # 1b 5C => ESC + BS
    s/\x1b\][0-9;]*[\x20-\x7e]*\([\x07\x9C]\|\x1b\\\)//g;
    # Safety: Remove all escape sequences https://superuser.com/questions/380772/removing-ansi-color-codes-from-text-stream
    s/\x1b\[[0-9;]*[a-zA-Z]//g;
  ' <<< "$ps")
\x01'*([^

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.


\x02'])

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.


\x02'}
  echo -n "$ps" | xxd >&2 

  >&2 echo -e "\nP3: Checking"
  if [[ "$ps" =~ [\x07\x1b\x9c] ]]; then
    # Check if escape inside
    # 07 => BEL
    # 1b => ESC
    # 9C => ST
    >&2 echo 'Warning: There is an escape code in your PS1 which is not betwwen \[ \]'
    >&2 echo "Tip: put \[ \] around your escape codes (ctlseqs + associated parameters)"
    echo -n "$ps" | xxd >&2
  # Check printable characters <= 20 .. 7e, and newline
  # -- Remove the trailing 0x0a (BEL)
  elif [[ "$ps" =~ [^[:graph:][:space:]] ]]; then
    >&2 echo 'Warning: There is a non printable character in PS1 which is not between \[ \]'
    >&2 echo "Tip: put \[ \] around your escape codes (ctlseqs + associated parameters)"
    echo "$ps"
    echo -n "$ps" | xxd >&2 
  fi

  # Echo result
  echo -n "${#ps}"
}

ps1_size

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.

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