Sample Header Ad - 728x90

zsh can't input to terminal when piping stdin and stdout with variable command that has tty output

18 votes
2 answers
3502 views
System info: 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) Scroll to the **EXAMPLES** at the bottom if you just want to dig in to the simplified examples that I made. NOTE: I am not a big zsh user. --- I was looking at the [fzf](https://github.com/junegunn/fzf) keybindings for [bash](https://github.com/junegunn/fzf/blob/master/shell/key-bindings.bash#L59) and [zsh](https://github.com/junegunn/fzf/blob/master/shell/key-bindings.zsh#L73) . Notice how they both run a variable command $(__fzfcmd). __fzfcmd by default outputs fzf to stdout and the parameter substitution just runs command (fzf) resulting from the output. One difference between the bash and zsh script is that the bash one further pipes the output of $(__fzfcmd) but zsh just captures it inside an array. My guess is because of a problem in zsh when you further pipe the output of fzf where you can't input to fzf and the process piped to by fzf doesn't get any stdin. Your only choice is to ^Z or ^C. ^C seems to background the process for some reason. Or maybe they just wanted it in an array so they could could [run zle vi-fetch-history on it](https://github.com/junegunn/fzf/blob/master/shell/key-bindings.zsh#L78) . The bash version does some magic in the key binding with ["\e^": history-expand-line](https://github.com/junegunn/fzf/blob/master/shell/key-bindings.bash#L83) Now fzf isn't important. It seems like you just need a program that outputs to the tty to be called by parameter substitution to cause this problem. So I will show some simpler examples. Here are some other commands that output to the tty that can cause this problem in zsh: - [vipe](https://linux.die.net/man/1/vipe) (run editor in middle of a pipe) - 'vim -' (make vim read from stdin. similar to vipe but won't output to stdout) --- In the examples below, replace every occurrence of vipe with vim - if you don't want to do a separate install. Just remember that vim - won't output the editor contents to stdout like vipe does. **EXAMPLES:** 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 >(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 Now, I'm mostly wondering why 2) has a problem for zsh but not for bash and why 4) and 5) fixes the problem for zsh. The requirements for zsh to have this problem to seem to be exactly what I put in the title: - input pipe - command run by variable/parameter substitution that has tty output - output pipe **UPDATE** I added another workaround that doesn't cause zsh to have this problem, 5). It's similar to 4) but instead of redirecting stdout directly into stin, I redirect it into a file that redirects into stdin using process substitution.
Asked by dosentmatter (536 rep)
Dec 18, 2017, 10:44 PM
Last activity: Jul 24, 2025, 04:48 AM