当使用具有 tty 输出的变量命令管道 stdin 和 stdout 时,zsh 无法输入到终端

当使用具有 tty 输出的变量命令管道 stdin 和 stdout 时,zsh 无法输入到终端

系统信息:

macOS Sierra 10.12.6
zsh 5.4.2 (x86_64-apple-darwin16.7.0)
GNU bash, version 4.4.12(1)-release (x86_64-apple-darwin16.3.0)

滚动到例子如果您只想深入研究我所做的简化示例,请在底部。

注意:我不是一个大zsh用户。


我正在看fzf按键绑定bashzsh

注意它们都是如何运行变量命令的$(__fzfcmd)__fzfcmd默认情况下,输出fzf到 stdout,参数替换仅运行fzf输出结果的 command ( ) 。

bash和脚本之间的一个区别zsh是,脚本bash进一步通过管道传输 的输出$(__fzfcmd),但zsh只是将其捕获到数组中。我的猜测是因为当您进一步通过管道传输无法输入zsh的输出并且通过管道传输到的进程没有获得任何标准输入时出现问题。您唯一的选择是或。似乎出于某种原因在后台处理该过程。或者也许他们只是想把它放在一个数组中,这样他们就可以fzffzffzf^Z^C^Czle vi-fetch-history在上面。该bash版本在按键绑定方面发挥了一些作用"\e^": history-expand-line

现在fzf不重要了。看来您只需要一个输出到tty由参数替换调用的程序即可导致此问题。所以我将展示一些更简单的例子。

以下是输出到tty可能导致此问题的其他一些命令zsh

  • 毒蛇(在管道中间运行编辑器)
  • 'vim -'(使vim从stdin读取。类似于vipe,但不会输出到stdout)

在下面的示例中,如果您不想进行单独安装,请将出现的所有 替换vipe为。vim -请记住,它vim -不会像那样将编辑器内容输出到标准输出vipe

例子:

1) echo 1 | vipe | cat            # works in both bash and zsh
2) echo 1 | $(echo vipe) | cat    # works in bash only. zsh problem with no output until I hit `^C`:
   ^C
   zsh: done                    echo 1 | 
   zsh: suspended (tty output)  $(echo vipe) | 
   zsh: interrupt               cat
   # seems like the process is backgrounded. I can still see it in jobs command

3) cat <(echo 1 | $(echo vipe))   # zsh and bash has the problem. I'm guessing because
                                  # the file isn't finished writing and cat is
                                  # blocking vipe's tty output
                                  # both their `^C` output is just:
   ^C # nothing special, as expected

4) cat < <(echo 1 | $(echo vipe)) # works in both bash and zsh
5) echo 1 | $(echo vipe) > >(cat) # works in both bash and zsh

# The following don't have and input pipe to vipe.
# Type something then send EOF with ^D
6) vipe | cat                     # works for both
7) $(echo vipe) | cat             # works for both

现在,我主要想知道为什么2)有问题zsh但没有问题bash以及为什么4)5)解决了问题zsh

出现这个问题的要求zsh似乎正是我在标题中提出的:

  • 输入管
  • tty通过具有输出的变量/参数替换运行的命令
  • 输出管

更新

我添加了另一种不会导致zsh此问题的解决方法5)。它类似于,4)但不是stdout直接重定向到stin,而是将其重定向到一个文件,该文件重定向到stdin使用进程替换。

答案1

我相信你的问题归结为不正确地引用你的扩展。

引用自zsh:14 扩展

$(...)括在括号中且前面带有美元符号(如)或用重音符号(如 ' ')引用的命令 ...将被替换为其标准输出,并删除所有尾随换行符。如果替换未用双引号括起来,则使用 IFS 参数将输出分解为单词。替代品$(cat foo)可能会被等效但更快的替代品所取代$(<foo)。在任何一种情况下,如果设置了 GLOB_SUBST 选项,则输出符合文件名生成的条件。

请注意,问题中的示例#2 会导致 NULL 的无限回显,原因是:

如果替换未用双引号括起来,则使用 IFS 参数将输出分解为单词。

换句话说,shell 无限地等待echo,因为默认分隔符是 SPACE,所以 echo 永远不会完成。 请参阅 TLDP:内部变量。这为cat命令留下了一个挂起的管道。

直觉上,我相信 4 和​​ 5 是由于输出重定向而起作用的。

相关内容