【问题标题】:Zsh prompt showing last error code only onceZsh 提示仅显示最后一次错误代码
【发布时间】: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(),因此没有适用于两者的解决方案。

标签: zsh


【解决方案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

【讨论】:

【解决方案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 -- ''${(%):-"%F{160}✘ exit status $code%f"}''
}
autoload -Uz add-zsh-hook
add-zsh-hook preexec track-exec-command
add-zsh-hook precmd print-exit-code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    • 2019-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多