Zsh 提示仅显示最后一次错误代码

Posted

技术标签:

【中文标题】Zsh 提示仅显示最后一次错误代码【英文标题】:Zsh prompt showing last error code only once 【发布时间】:2018-03-18 11:49:30 【问题描述】:

我希望我的提示在上一个命令失败时显示一个叉号 (✘)。我使用以下代码:

export PROMPT=$'%(?..✘\n)\n› '

这给了我以下输出:

› echo Hello
Hello

› asjdfiasdf
zsh: command not found: asjdfiasdf
✘

› 
✘

我想对提示进行修改,使其在Enter后重绘提示时不重复打叉(上例中的第三种情况)。

有可能吗?

【问题讨论】:

你使用 bash 还是 zsh? Zsh。 @TarunLalwani 编辑了标签并添加了 bash。我不认为这是一个合适的标签。 @MariánČerný,我添加该标签的原因是,这个问题可能需要使用 bash 的人的专家评论,并且该解决方案可能适用于 bash 和 zsh。只是为了确保专家不会错过它,我这样做了 @TarunLalwani 谢谢。你有更高的声誉,所以我相信你的决定。 @TarunLalwani – 我认为 bash 关键字在这里没有任何意义。由于 bash 使用 $PROMPT_COMMAND 而 zsh 使用 preexec()precmd(),因此没有适用于两者的解决方案。 【参考方案1】:

我想我明白了。如果您发现错误,请告诉我...

preexec() 
    preexec_called=1

precmd() 
    if [ "$?" != 0 ] && [ "$preexec_called" = 1 ]
    then echo ✘; unset preexec_called; fi

PROMPT=$'\n› '

结果:

› ofaoisfsaoifoisafas
zsh: command not found: ofaoisfsaoifoisafas
✘  

› 

› echo $? # (not overwritten)
127

【讨论】:

不错。我之前没有尝试过preexec。奇迹般有效。我稍微更新了您的代码,因此没有 ret 变量。并添加了一个换行符。 如果你想让十字架变成红色,使用:echo "$fg_bold[red]✘$reset_color" 参考 9.3.1 挂钩函数:zsh.sourceforge.net/Doc/Release/Functions.html,所有此类函数的列表:zsh.sourceforge.net/Doc/Release/Functions-Index.html【参考方案2】:

我在我的 zsh 中执行此操作,但使用颜色而不是 unicode 字符。原理是一样的。

首先,我设置我的颜色,确保它们仅在受支持时使用:

case $TERM in

( rxvt* | vt100* | xterm* | linux | dtterm* | screen )
  function PSC()  echo -n "%\e[$*m%";  # insert color-specifying chars
  ERR="%(0?,`PSC '0;32'`,`PSC '1;31'`)"      # if last cmd!=err, hash=green, else red
  ;;

( * )
  function PSC()  true;    # no color support? no problem!
  ERR=
  ;;

esac

接下来,我设置了一个神奇的回车功能(感谢this post about an empty command(忽略问题,看我这里如何改编):

function magic-enter()     # from https://superuser.com/a/625663
  if [[ -n $BUFFER ]]
    then unset Z_EMPTY_CMD  # Enter was pressed on an empty line
    else Z_EMPTY_CMD=1      # The line was NOT empty when Enter was pressed
  fi
  zle accept-line           # still perform the standard binding for Enter

zle -N magic-enter          # define magic-enter as a widget
bindkey "^M" magic-enter    # Backup: use ^J

现在是解释捕获命令并使用其返回码设置提示颜色的时候了:

setopt prompt_subst # allow variable substitution

function preexec()  # just after cmd has been read, right before execution
  Z_LAST_CMD="$1"   # since $_ is unreliable in the prompt
  #Z_LAST_CMD="$1[(wr)^(*=*|sudo|-*)]"    # avoid sudo prefix & options
  Z_LAST_CMD_START="$(print -Pn '%D%s.%.')"
  Z_LAST_CMD_START="$Z_LAST_CMD_START%." # zsh <= 5.1.1 makes %. a literal dot
  Z_LAST_CMD_START="$Z_LAST_CMD_START%[%]" # zsh <= 4.3.11 makes %. literal


function precmd()  # just before the prompt is rendered
  local Z_LAST_RETVAL=$?                  # $? only works on the first line here
  Z_PROMPT_EPOCH="$(print -Pn '%D%s.%.')"  # nanoseconds, like date +%s.%N
  Z_PROMPT_EPOCH="$Z_PROMPT_EPOCH%."    # zsh <= 5.1.1 makes %. a literal dot
  Z_PROMPT_EPOCH="$Z_PROMPT_EPOCH%[%]"  # zsh <= 4.3.11 makes %. a literal %.
  if [ -n "$Z_LAST_CMD_START" ]; then
    Z_LAST_CMD_ELAPSED="$(( $Z_PROMPT_EPOCH - $Z_LAST_CMD_START ))"
    Z_LAST_CMD_ELAPSED="$(printf %.3f "$Z_LAST_CMD_ELAPSED")s"
  else
    Z_LAST_CMD_ELAPSED="unknown time"
  fi

  # full line for error if we JUST got one (not after hitting <enter>)
  if [ -z "$Z_EMPTY_CMD" ] && [ $Z_LAST_RETVAL != 0 ]; then
    N=$'\n'  # set $N to a literal line break
    LERR="$N$(PSC '1;0')[$(PSC '1;31')%D%Y/%m/%d %T$(PSC '1;0')]"
    LERR="$LERR$(PSC '0;0') code $(PSC '1;31')$Z_LAST_RETVAL"
    LERR="$LERR$(PSC '0;0') returned by last command"
    LERR="$LERR (run in \$Z_LAST_CMD_ELAPSED):$N"
    LERR="$LERR$(PSC '1;31')\$Z_LAST_CMD$(PSC '0;0')$N$N"
    print -PR "$LERR"
  fi

最后设置提示:

PROMPT="$(PSC '0;33')[$(PSC '0;32')%n@%m$(PSC '0;33') %~$PR]$ERR%#$(PSC '0;0') "

它的外观如下:

 

对问题的更直接的回答,改编自上文:

function magic-enter()     # from https://superuser.com/a/625663
  if [[ -n $BUFFER ]]
    then unset Z_EMPTY_CMD  # Enter was pressed on an empty line
    else Z_EMPTY_CMD=1      # The line was NOT empty when Enter was pressed
  fi
  zle accept-line           # still perform the standard binding for Enter

zle -N magic-enter          # define magic-enter as a widget
bindkey "^M" magic-enter    # Backup: use ^J

function precmd()  # just before the prompt is rendered
  local Z_LAST_RETVAL=$?                  # $? only works on the first line here

  # full line for error if we JUST got one (not after hitting <enter>)
  if [ -z "$Z_EMPTY_CMD" ] && [ $Z_LAST_RETVAL != 0 ]; then
    echo '✘'
  fi


PROMPT=$'\n› '

屏幕截图:

【讨论】:

我不小心在第一个屏幕截图的 prompt.zsh 源代码中包含了最后一行 RPROMPT="$(PSC '0;34')%w %*$(PSC '0;0')"。当我从我在这里发布的内容中删除它时,我没有注意到它在那里,因为它不相关。 很高兴知道还有其他选择。我以前见过一些小部件和绑定。以我目前的 zsh 知识,我发现 @sneep 的解决方案更容易理解。顺便说一句,我的 Unicode 交叉字符使用红色:-D。【参考方案3】:

使用 prexec 和 precmd 钩子:

在任何命令执行之前调用 preexec 钩子。没有执行命令时不会调用它。例如,如果您在空提示符或只有空格的提示符处按 Enter,则不会调用它。调用此钩子表示命令已运行。

在显示提示之前调用 precmd 钩子以收集下一个命令。在打印提示之前,您可以打印出退出状态。在这里我们可以检查一个命令是否刚刚执行,以及是否有我们想要显示的状态码。

这与@sneep 建议的解决方案非常相似,这也是一个很好的解决方案。不过,使用这些钩子是值得的,这样如果您有其他任何东西注册这些钩子,他们也可以这样做。

# print exit code once after last command output
function track-exec-command() 
  zsh_exec_command=1

function print-exit-code() 
  local -i code=$?
  (( code == 0 )) && return
  (( zsh_exec_command != 1 )) && return
  unset zsh_exec_command
  print -rC1 -- ''$(%):-"%F160✘ exit status $code%f"''

autoload -Uz add-zsh-hook
add-zsh-hook preexec track-exec-command
add-zsh-hook precmd print-exit-code

【讨论】:

以上是关于Zsh 提示仅显示最后一次错误代码的主要内容,如果未能解决你的问题,请参考以下文章

VSCODE 构建错误`终端进程“/bin/zsh '-c', 'yarn run watch-extensionsd'”无法启动(退出代码:127)。

显示错误zsh:分段错误。如何改变?

jQuery-UI 对话框仅显示 for 循环的最后一次迭代 [重复]

微信分享提示signature错误

jQuery-UI对话框仅显示for循环的最后一次迭代[duplicate]

如何检查之前的 Zsh 提示符中是不是没有输入任何内容