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

dos*_*ter 14 bash zsh pipe tty

系统信息:

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)
Run Code Online (Sandbox Code Playgroud)

滚动到示例在底部,如果你只是想挖成简单的例子,我做。

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


我一直在寻找在fzf按键绑定的bashzsh

注意它们是如何运行可变命令的$(__fzfcmd)__fzfcmd默认情况下输出fzf到 stdout 并且参数替换仅运行fzf由输出产生的命令 ( )。

bashzsh脚本之间的一个区别是,bash它进一步通过管道传输 的输出,$(__fzfcmd)zsh只是将其捕获在数组中。我的猜测是因为zsh当您进一步将fzf无法输入的输出fzf通过管道传输到并且通过管道传输到的进程fzf没有获得任何标准输入时出现问题。您唯一的选择是^Z^C^C似乎出于某种原因使该过程成为背景。或者也许他们只是想将它放在一个数组中,以便他们可以在它上面运行zle vi-fetch-history。该bash版本在键绑定中做了一些魔法"\e^": history-expand-line

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

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

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

在下面的示例中,如果您不想进行单独安装,请将出现的每个替换为vipewith 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
Run Code Online (Sandbox Code Playgroud)

现在,我主要是想知道为什么2)有一个问题zsh,但不是bash,为什么4)5)修复问题zsh

对于要求zsh有这个问题似乎正是我把标题:

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

更新

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

eyo*_*100 0

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

\n\n

引用自zsh:14 扩展

\n\n
\n

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

\n
\n\n

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

\n\n
\n

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

\n
\n\n

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

\n\n

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

\n