我正在尝试配置 tmux,使其在按下“enter”键时始终刷新客户端(或客户端状态栏)。我目前正在使用
bind -n enter send-key Enter; refresh -S
但行为很奇怪。它确实Enter
每次都发送,因此我可以正常使用密钥,但并不refresh -S
总是执行。我使用状态栏设置测试配置
set -g status-left "#{pane_current_path}"
st 它应该在输入cd
命令时立即更新。但这并没有发生。当输入cd
到某个地方时,状态栏只会在标准周期内更新,即有时会延迟。如果我在输入另一个命令(或空白行)后不久输入另一个命令,cd
状态栏确实会立即更新。
send-key KPEnter
使用 而不是 ,也会发生同样的事情Enter
。
有人知道这里发生什么事吗?
答案1
这是一种竞争条件。
扩展#{pane_current_path}
需要 tmux 服务器了解进程的当前工作目录是窗格中的“主目录”。tmux 服务器如何了解或已经知道这是哪个进程并不重要,重点是该进程不是 tmux 的一部分,它是一个单独的进程。
通常,就像您的情况一样,窗格中的“主进程”是一个交互式外壳。
Tmux 服务器可以通过检查 来了解此 shell 的当前工作目录/proc/<PID>/cwd
,其中<PID>
表示 shell 的 PID;或者通过一些等效方法。
当你输入cd whatever
Enter并send-key Enter; refresh -S
运行后,send-key Enter
shell 开始解释cd whatever
。重要的是send-key
,它只发送键,不等待此操作的任何效果;而 shell 只是开始解释cd whatever
、解释和执行需要一些时间;但是毫不拖延tmux 继续执行,refresh -S
使 tmux 服务器开始刷新客户端的状态行,这涉及评估#{pane_current_path}
。shell(由 触发send-key
)和 tmux 服务器(由 触发refresh
)执行其任务在平行下并且无法保证 shell 在#{pane_current_path}
评估之前能够真正改变其当前工作目录。
如果首先评估格式字符串,那么您将在状态行中看到旧的工作目录。
cd
在makes#{pane_current_path}
返回新的当前工作目录后不久,另一个命令或一个空白行会因为Enter这里已经足够晚了,shell 已经设法改变了其工作目录。
cd
在交互式 shell 中,促使状态行无竞争地刷新的一个好方法是,在它实际更改工作目录后cd
触发自身:refresh -S
if [ -n "$TMUX" ]; then
cd () {
command cd "$@"
(
status="$?"
tmux refresh -S
return "$status"
)
}
fi
如果 shell 位于 tmux 内部,则上述代码片段将创建一个重载cd
内置函数的 shell 函数。
笔记:
- shell 代码本身是可移植的。唯一不可移植的部分是 的调用
tmux
。 - 的别名的存在
cd
将导致代码片段失败或行为异常。在 Bash 中,您可以通过将其替换cd ()
为不可移植的来解决此问题function cd
。 - 该函数保留实际的退出状态
cd
。 - 异步调用
tmux
(tmux refresh -S &
) 将使 shell 不等待它,但不会破坏解决方案。我指出这一点是因为在你最初尝试使用bind
shell 时,它也不会等待,所以或许这是您喜欢的。但是,如果异步tmux
失败(虽然可能性很小)并打印错误消息,则该消息可能会在下一个 shell 提示符后出现,这会令人困惑。出于这个原因,在我看来更加优雅同步调用tmux
。异步调用确实tmux
可以节省几毫秒;不过在交互式 shell 中,您很可能不会注意到差异。