【问题标题】:How to print current bash prompt?如何打印当前的 bash 提示符?
【发布时间】:2014-04-14 21:31:31
【问题描述】:

问题很简单。我想在我的 bash 脚本中评估 PS1 的当前值。

google 上的所有材料都指向关于拉皮条的教程,但我想评估一下我当前的终端或至少 一些 终端如何呈现它。

是否有任何软件/功能可以帮助我实现这一目标?当然,我希望评估所有转义字符,所以 echo $PS1 在我的情况下没有那么有用。

【问题讨论】:

标签: bash prompt ps1


【解决方案1】:

编辑 /etc/bashrc 文件

您可以以此为例并检查输出

    # If id command returns zero, you’ve root access.
if [ $(id -u) -eq 0 ];
then # you are root, set red colour prompt
  PS1="\\[$(tput setaf 1)\\]\\u@\\h:\\w #\\[$(tput sgr0)\\]"
else # normal
  PS1="[\\u@\\h:\\w] $"
fi

【讨论】:

  • 但这并不能真正解决我的任何问题。我无法以编程方式获取将显示为命令提示符的字符串。
【解决方案2】:

另一种可能性,使用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 formatted prompt properly here>

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

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

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

编辑:
似乎script 的新版本与打字稿中的管道stdin 相呼应。为了解决这个问题,可以将上述机制更改为:

$ 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 '{old=current; current=$0;} END{print old}' RS=$RANDOM_STRING
PS1="$TEST_PS1"; HISTFILE=/dev/null
alias $RANDOM_STRING=true
$RANDOM_STRING
$RANDOM_STRING
EOF

<prints the formatted prompt properly here>

说明:

尝试在终端上手动输入这些命令。按原样复制heredoc 下的这些命令,然后单击鼠标中键进行粘贴。脚本命令的标准输出将包含非常相似的内容。

例如在上述情况下,脚本命令的输出如下:

PS1="\e[31;1m\u@\h:\n\e[0;1m$ \e[0m"; HISTFILE=/dev/null
alias some_random_string_here_that_is_not_part_of_PS1=true
some_random_string_here_that_is_not_part_of_PS1
some_random_string_here_that_is_not_part_of_PS1
 \e[0m"; HISTFILE=/dev/nullhsane-dev : ~/Desktop $ PS1="\e[31;1m\u@\h:\n\e[0;1m$ 
anishsane@anishsane-dev:
$ alias some_random_string_here_that_is_not_part_of_PS1=true
anishsane@anishsane-dev:
$ some_random_string_here_that_is_not_part_of_PS1
anishsane@anishsane-dev:
$ some_random_string_here_that_is_not_part_of_PS1
anishsane@anishsane-dev:
$ exit

使用“some_random_string_here_that_is_not_part_of_PS1”作为分隔符(awk 的记录分隔符)拆分该标准输出并打印最后一条记录。

EDIT2:

另一种机制(使用bash源代码和gdb):

$ gdb -batch -p $$ -ex 'call bind_variable("expanded_PS1", decode_prompt_string (get_string_value ("PS1")), 0)'
$ echo "$expanded_PS1"
<prints the formatted prompt properly here>
  1. 这里有一个小问题。 PS1 中的 \[\] 字符串将分别打印为 \1/\2。你可以删除那些tr -d '\1\2' &lt;&lt;&lt; "$expanded_PS1"
  2. 如果您收到诸如 gdb failed to attach to the process 之类的错误(似乎发生在 ubuntu :-\ 中),请使用 sudo 运行 gdb

【讨论】:

  • +1 请注意,您需要使用less 之类的内容查看my_filename,以查看提示中的任何转义字符是否正确呈现。
  • 编辑为仅在屏幕上输出解析的PS1。 :)
  • 您可以使用colcrt 之类的方式部分清除转义码。它适用于 man 页面,而不是 script 输出,所以它并不完美,但它是一个开始。
  • echo -n $RANDOM_STRING 有什么作用?是必需的吗?为什么要执行两次? awk 对随机字符串做了什么?对上述命令如何工作的更多解释会很棒。
  • 添加说明。
【解决方案3】:

试试下面的命令

echo $PS1 | 
sed -e s/'\\d'/"$(date +'%a %b %_d')"/g | 
sed -e s/'\\t'/"$(date +'%T')"/g | 
sed -e s/'\\@'/"$(date +'%r')"/g | 
sed -e s/'\\T'/"$(date +'%r'| awk {'print $1'})"/g | 
sed -e s/'\\e'//g | sed -e s/'\\h'/"$HOSTNAME"/g | 
sed -e s/'\\h'/"$HOSTNAME"/g | 
sed -e s/'\\H'/"$HOSTNAME"/g | 
sed -e s/'\\u'/"$USER"/g | 
sed -e s@'\\W'@"$(pwd)"@g | 
sed -e s/'\\w'/"$(pwd | sed -e s@$HOME@'~'@g )"/g | 
sed -e s/"\\\\"//g | 
sed -e s/"\\["//g | 
sed -e s/"\\]"/*/g | 
cut -d'*' -f2 | 
cut -d';' -f2 | 
sed s/\ //g | 
sed -e s/[a-z]$/"$([ "$USER" != "root" ] && echo \$ || echo \#)"/g

【讨论】:

  • 你能解释一下如果你的代码运行会发生什么吗?在我们知道它应该做什么之前,我们中的一些人不想只是“尝试”。 :-)
  • 我不喜欢这个答案,但看起来他们正在搜索 PS1 转义序列,并用正确的值替换它们。它并没有像其他人那样真正检查框
【解决方案4】:

另一种方法是eval 回应您处理任何扩展的提示(不确定为什么保留括号)。这很可能不如@anishsane 的方法稳健,但可能会快一点:

show-prompt() {
    eval 'echo -en "'$PS1'"' | sed -e 's#\\\[##g' -e 's#\\\]##g'
}
# To show it in a function registered with `complete -F` on
# a single tab, and keep the user's input:
show-prompt
echo -n "${COMP_WORDS[@]}"

已经对这个GitHub issue进行了修改。

【讨论】:

  • “注册complete -F”到底是什么意思?我正在尝试从 Octave 中使用它,以便显示常规提示,然后将用户的输入作为命令存储,但提示不显示;像这样:octave --eval "system('./showprompt'); usercommand = input('', 's')"
  • complete -F 是用于制表符补全的 bash 习语;要查看更多信息,请参阅bash -c 'help complete'。我上面的评论很简洁,但我的意思是你可以将这两行注入到传递给 bash 的complete -F &lt;function&gt; 的函数中。通过这种方式,您可以创建一个具有长时间运行更新的完成方法(例如,初始化 bazel 构建系统的 CLI),但您至少可以向用户显示它正在工作,而不仅仅是冻结。不幸的是,对于您使用 Octave 的特定应用程序,我没有任何建议:(
  • 这似乎对我的提示有用,除了转义序列 \u\h\W (我碰巧在我的 PS1 变量中;也许还有更多的序列,例如这些)。
【解决方案5】:

我会这样理解:

echo $PS1

然后使用编辑器对其进行编辑。之后进行测试(在会话处于活动状态时设置):

PS1='\[\033[1m\]\[\033[34m\]\u\[\033[90m\]@\[\033[01;35m\]\h:\[\033[01;32m\]\W\[\033[0m\]$ '

(\u 代表用户,\h 代表主机,\w 代表完整路径,\W 代表短路径)

如果我喜欢它,我将通过更改 ~/.bashrc

PS1 的值来使其永久化

附:

如果要查看所有全局变量:

printenv

或:

printenv <name_of_var_to_see>

【讨论】:

  • 但这正是我不想实现的——它打印出变量的原始值,我需要渲染它——即所有颜色、\x 占位符等被评估. @anishsane 答案,无论多么复杂,都可以正常工作。在 cmets 中链接的“可能重复”问题中发布了一个更好的方法。
  • 执行第二步。我为您提供了一种查看当前版本的方法,以便您可以在需要时恢复到它。第二步是 PS1=....
【解决方案6】:

Bash 4.4+ 解决方案对提示字符串使用参数转换:echo "${PS1@P}"

[adamhotep@tabasco ~]$ echo "the prompt is '${PS1@P}'"
the prompt is '[adamhotep@tabasco ~]$'
[adamhotep@tabasco ~]$ TEST_STRING='\u is dining at \t using \s \V'
[adamhotep@tabasco ~]$ echo "${TEST_STRING}"
\u is dining at \t using \s \V
[adamhotep@tabasco ~]$ echo "${TEST_STRING@P}"
adamhotep is dining at 21:45:10 using bash 5.0.3
[adamhotep@tabasco ~]$ 

来自Shell Parameter Expansion上的Bash Reference Manual页面:

${parameter@operator}

参数转换。扩展要么是参数值的转换,要么是关于参数本身的信息,具体取决于运算符的值。
每个运算符都是一个字母:

Q  The expansion is a string that is the value of parameter quoted in a
   format that can be reused as input.
E  The expansion is a string that is the value of parameter with backslash
   escape sequences expanded as with the $'…' quoting mechanism.
P  The expansion is a string that is the result of expanding the value of
   parameter as if it were a prompt string (see PROMPTING below).
A  The expansion is a string in the form of an assignment statement or
   declare command that, if evaluated, will recreate parameter with its
   attributes and value.
a  The expansion is a string consisting of flag values representing
   parameter's attributes.

如果参数为@*,则依次对每个位置参数进行运算,展开为结果列表。如果 parameter 是下标为@* 的数组变量,则依次对数组的每个成员进行运算,展开就是结果列表。

(另请参阅来自重复问题 Echo expanded PS1this answer。)

 

Z Shell (zsh) 可以使用 ${(%%)PS1} 或其 print 内置的 -P 标志来做到这一点:

[adamhotep@tabasco ~]% echo "the prompt is '${(%%)PS1}'"
the prompt is '[adamhotep@tabasco ~]%'
[adamhotep@tabasco ~]% print -P "the prompt is '$PS1'"
the prompt is '[adamhotep@tabasco ~]%'
[adamhotep@tabasco ~]% TEST_STRING="%n is dining at %* using %N $ZSH_VERSION"
[adamhotep@tabasco ~]% echo "$TEST_STRING"
%n is dining at %* using %N 5.7.1
[adamhotep@tabasco ~]% echo "${(%%)TEST_STRING}"
adamhotep is dining at 11:49:01 using zsh 5.7.1
[adamhotep@tabasco ~]% print -P "$TEST_STRING"
adamhotep is dining at 11:49:07 using zsh 5.7.1
[adamhotep@tabasco ~]% 

Zsh Expansion and Subsitution 手册告诉我们:

Parameter Expansion Flags。如果左大括号后直接跟左括号,则匹配右括号之前的字符串将被视为标志列表。在重复标志有意义的情况下,重复不必是连续的;例如,(q%q%q) 与更易读的(%%qqq) 含义相同。支持以下标志:

%   以与 提示(请参阅Prompt Expansion)。如果这个标志被给出两次,完整的 对生成的单词进行快速扩展,具体取决于 PROMPT_PERCENTPROMPT_SUBSTPROMPT_BANG 选项的设置。

来自Zsh Builtinsprint 的文档:

-P   执行提示扩展(参见Prompt Expansion)。结合-f,提示转义序列仅在插值参数内进行解析,而不是在格式字符串内。

【讨论】:

  • 这绝对是这里最好的答案,简单且工作正常,谢谢。
  • 非常彻底的答案!谢谢!
  • 注意 PS1 不是 ENV var,您无法导出它,使用 bash 手动传递它会过滤掉它...不要尝试。只需将其重命名为 PSX 之类的其他名称即可
  • 我在使用 bash 5.1.8 时遇到了一个错误的替换错误,有人可以解决这个问题吗?
  • @DanielPorteous – 我认为作为一个新的 SO 问题可能会更好。如果您确实将其作为问题发布(例如您的 $PS 和您的用例!),请在此处添加链接到它的评论,然后我们可以从今天开始删除其他 cmets 以减少此空间中的喋喋不休。
猜你喜欢
  • 1970-01-01
  • 2012-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多