You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Zsh ZLE函数使用`trap 'zle reset-prompt' EXIT`时触发段错误的问题咨询

Zsh ZLE函数使用trap 'zle reset-prompt' EXIT时触发段错误的问题咨询

嗨,我来帮你梳理这个问题的来龙去脉~

首先,你遇到的这个段错误问题,确实和trap 'zle reset-prompt' EXIT的使用有关,这属于Zsh在处理ZLE(Zsh Line Editor)组件与EXIT陷阱交互时的已知边缘场景问题,不少用户都反馈过类似的崩溃情况,不能完全算严格意义上的“bug”,但确实是Zsh内部状态管理和ZLE生命周期不匹配导致的冲突。

问题根源分析

当你的tmx作为ZLE widget运行时,Zsh会维护一套专门的内部状态来处理命令行编辑。而你设置的EXIT陷阱会在函数退出时触发,但触发时机可能刚好落在ZLE内部状态已经开始清理、或者处于不稳定的阶段——这时候调用zle reset-prompt这类操作ZLE状态的命令,就很容易导致内存访问错误,也就是你看到的段错误。

比如当你用Ctrl-C中断fzf、或者detach tmux会话时,函数的退出路径比较特殊,EXIT陷阱触发时ZLE的上下文已经不是正常运行时的稳定状态,这时候调用ZLE命令就会出问题。

你的Workaround为什么有效

你用{ ... } always { zle reset-prompt }的方式替代EXIT陷阱,是非常合理的解决思路:

  • Zsh的always块会在包裹的代码块无论正常退出还是异常终止时执行,而且执行时机是在函数的ZLE上下文还保持稳定的阶段,不会像EXIT陷阱那样在状态已经开始瓦解的时候触发。
  • 这样既达到了退出后重置提示符的目的,又避开了ZLE状态不稳定的风险,自然就不会出现段错误了。

补充建议

在编写ZLE widget时,尽量避免使用EXIT陷阱来执行ZLE相关命令,优先选择Zsh提供的always块、或者zle -F这类专门的ZLE回调机制,它们更适配ZLE的生命周期管理。

下面是你提供的相关代码整理:

原问题中的tmx函数:

function tmx {
  [ -n "$ZLE_STATE" ] && trap 'zle reset-prompt' EXIT

  local tmux item

  tmux="$(which tmux)" || return $?

  if [ -z "$1" ] ; then
    # The 1st value is the query string.
    # The 2nd value is the selected, matched value, or empty if none.
    $tmux list-sessions -F '#{session_name}' |\
    fzf --header jump-to-session --print-query \
    --preview "$tmux capture-pane -ept {}" |\
    while read ; do  # Pick the last non-empty value.
      [ -n "$REPLY" ] && item="$REPLY"
    done
  else
    item="$1"
  fi

  [ -z "$item" ] && return 1

  (
    # Restore the standard std* file descriptors for tmux.
    exec </dev/tty; exec <&1;
    if [ -z "$TMUX" ] ; then
      $tmux new-session -As "$item"
    else
      $tmux has-session -t "$item" || \
      $tmux new-session -ds "$item"
      $tmux switch-client -t "$item"
    fi
  )
}

zle -N tmx
bindkey "$(tput kf4)" tmx

有效的Workaround版本:

function tmx {
  {
    # function body up to `exec` ...
    local tmux item

    tmux="$(which tmux)" || return $?

    if [ -z "$1" ] ; then
      $tmux list-sessions -F '#{session_name}' |\
      fzf --header jump-to-session --print-query \
      --preview "$tmux capture-pane -ept {}" |\
      while read ; do
        [ -n "$REPLY" ] && item="$REPLY"
      done
    else
      item="$1"
    fi

    [ -z "$item" ] && return 1
  } always {
    zle reset-prompt
  }

  (
    exec </dev/tty; exec <&1;
    if [ -z "$TMUX" ] ; then
      $tmux new-session -As "$item"
    else
      $tmux has-session -t "$item" || \
      $tmux new-session -ds "$item"
      $tmux switch-client -t "$item"
    fi
  )
}

备注:内容来源于stack exchange,提问作者Petr

火山引擎 最新活动