变量HISTCMD 会在每次执行新命令时更新。不幸的是,在PROMPT_COMMAND 的执行过程中,该值被屏蔽了(我想是因为历史记录没有与提示命令中发生的事情混淆)。我想出的解决方法有点混乱,但它似乎在我有限的测试中有效。
# This only works if the prompt has a prefix
# which is displayed before the status code field.
# Fortunately, in this case, there is one.
# Maybe use a no-op prefix in the worst case (!)
PS1_base=$'\n'
# Functions for PROMPT_COMMAND
PS1_update_HISTCMD () {
# If HISTCONTROL contains "ignoredups" or "ignoreboth", this breaks.
# We should not change it programmatically
# (think principle of least astonishment etc)
# but we can always gripe.
case :$HISTCONTROL: in
*:ignoredups:* | *:ignoreboth:* )
echo "PS1_update_HISTCMD(): HISTCONTROL contains 'ignoredups' or 'ignoreboth'" >&2
echo "PS1_update_HISTCMD(): Warning: Please remove this setting." >&2 ;;
esac
# PS1_HISTCMD needs to contain the old value of PS1_HISTCMD2 (a copy of HISTCMD)
PS1_HISTCMD=${PS1_HISTCMD2:-$PS1_HISTCMD}
# PS1_HISTCMD2 needs to be unset for the next prompt to trigger properly
unset PS1_HISTCMD2
}
PROMPT_COMMAND=PS1_update_HISTCMD
# Finally, the actual prompt:
PS1='${PS1_base#foo${PS1_HISTCMD2:=${HISTCMD%$PS1_HISTCMD}}}${_:+${PS1_HISTCMD2:+$? }}$ '
提示中的逻辑大致如下:
${PS1_base#foo...}
这会显示前缀。 #... 中的内容仅对其副作用有用。我们希望在不显示变量值的情况下进行一些变量操作,因此我们将它们隐藏在字符串替换中。 (如果 PS1_base 的值恰好以 foo 开头,后跟当前命令历史索引,这将显示奇怪且可能很壮观的事情。)
${PS1_HISTCMD2:=...}
这会为PS1_HISTCMD2 分配一个值(如果它未设置,我们已确定它是)。替换名义上也会扩展到新值,但我们已将其隐藏在 ${var#subst} 中,如上所述。
${HISTCMD%$PS1_HISTCMD}
我们将HISTCMD 的值(当在命令历史记录中创建新条目时,即我们正在执行新命令时)或空字符串(当命令为空时)分配给PS1_HISTCMD2。这通过修剪值 HISTCMD 来实现PS1_HISTCMD 上的任何匹配(使用${var%subst} 后缀替换语法)。
${_:+...}
这是来自问题。如果$_ 的值被设置并且非空(当命令正在执行时,但不是例如,如果我们正在执行变量赋值),它将扩展为...。如果PS1_HISTCMD2 不为空,则“某物”应该是状态代码(以及一个空格,以便于阅读)。
${PS1_HISTCMD2:+$? }
那里。
'$ '
这只是实际的提示后缀,与原始问题一样。
所以关键部分是变量PS1_HISTCMD,它记住HISTCMD的先前值,以及变量PS1_HISTCMD2,它捕获HISTCMD的值,因此可以从PROMPT_COMMAND内部访问它,但需要在PROMPT_COMMAND 中取消设置,以便在下次显示提示时再次触发${PS1_HISTCMD2:=...} 分配。
我试图隐藏${PS1_HISTCMD2:=...} 的输出,但后来意识到实际上我们想要显示一些东西,所以只是捎带它。您不能有一个完全空的PS1_base,因为shell 显然会注意到,并且在没有值时甚至不会尝试执行替换;但是,如果您没有其他要显示的内容,也许您可以想出一个虚拟值(也许是无操作转义序列?)。或者也许可以将其重构为使用后缀运行;但这可能会更棘手。
为了响应 Anubhava 的“最小答案”挑战,这里是没有 cmets 或错误检查的代码。
PS1_base=$'\n'
PS1_update_HISTCMD () { PS1_HISTCMD=${PS1_HISTCMD2:-$PS1_HISTCMD}; unset PS1_HISTCMD2; }
PROMPT_COMMAND=PS1_update_HISTCMD
PS1='${PS1_base#foo${PS1_HISTCMD2:=${HISTCMD%$PS1_HISTCMD}}}${_:+${PS1_HISTCMD2:+$? }}$ '