从完成函数中读取 /dev/tty

从完成函数中读取 /dev/tty

为什么以下代码无法完成命令foo?当我输入它然后输入 时foo <Tab>,外壳会挂起并且不会接受任何输入,直到我按下^C(退出命令完成)。

我的假设是/dev/ttyshell 已经读取了它,并且它在某种程度上cat也影响了读取它的能力,但在这种情况下我仍然需要一个解决方法。

_foo() {
    _values 'foo' "$(cat < /dev/tty)"
}

compdef _foo foo

请注意,这个示例是故意简化的:实际用例是运行终端接口程序(想想 ncurses)而不是cat.

答案1

在完成过程中,您位于 zsh 行编辑器中,因此终端行规则自己的行编辑器被禁用,就像您运行了:

stty -icanon -echo

在该模式下,cat无法退出,因为您无法表示输入结束(^D是行规则行编辑器行为的一部分icanon),并且您不会看到您键入的内容的回显。

你可以这样做:

_foo() {
  _values 'foo' "$(
    {
      s=$(stty -g)
      stty sane
      cat
      stty $s
    } < /dev/tty)"
  zle -I
}

也就是说,在运行之前将终端设备置于预期的状态cat(可以^D按空行或按两次来结束输入)cat并在运行后恢复。我们告诉 zle 它必须重新绘制其提示符和缓冲区,因为您在行规则行编辑器中键入的内容的回显会使事情变得混乱zle -I无效)。

答案2

这可能太简单了,cat会读/dev/tty很长一段时间。随着_foo完成

#compdef foo
_values 'foo' "$(promptfor)"

promptfor一个

#!/usr/bin/env expect
set fh [open /dev/tty r+]
stty raw -echo
set key [read $fh 1]            ;# read from tty
puts stdout $key                ;# to ZSH
flush stdout
stty -raw echo

我的 ZSH onfootab将完成promptfor运行时按下的任何键。

如果您有某种守护进程,那么您可能需要一些更奇特的东西,例如 ZSH 和您的守护进程可以用来执行任何必要通信的套接字;read在 ZSH 中可以从任意文件描述符或协进程读取...

相关内容