I expected cat <(cat) and cat | cat to do the same thing: copy lines from stdin to stdout. My understanding was that both would execute a cat in a subshell, redirect the subshell cat's stdout to a temporary named pipe, and then execute another cat in the current shell with its stdin redirected to the pipe.

Instead, cat <(cat) lets me type at my terminal, but none of the input lines get copied and ^D fails to signal EOF; cat | cat works as expected though.

As a further experiment, I checked if cat =(cat) has similar difficulties as cat <(cat), but it works as I expected: all of stdin up to a ^D gets copied to stdout in one go.

Can anyone help me understand what zsh is doing under the hood?


1 Answer 1

  1. a | b connects STDOUT from a and STDIN from b just by using dup/dup2. Both commands are executed in parallel.

  2. a =(b) replaces the argument to a with an temporary filename. b will be executed before a as the temporary file needs to be created before it can be passed to a

  3. a <(b) replaces the argument to a with an named pipe. a and b run in parallel. This is now where it gets a little bit complicated:

    b is in the background and can't read from the terminal. You can test it yourself by using strace -p $PID to attach to your second cat process to see the process.

    a in the meantime tries to read from the named pipe but can't read anything as as b can't read.

    • This means you basically have a deadlock where a tries to read from b but b can't read from STDIN and can't write to a

More information about background process and terminal from man bash:

To facilitate the implementation of the user interface to job control, the operating system maintains the notion of a current terminal process group ID. Members of this process group (processes whose process group ID is equal to the current terminal process group ID) receive keyboard-generated signals such as SIGINT. These processes are said to be in the foreground. Background processes are those whose process group ID differs from the terminal's; such processes are immune to keyboard-generated signals. Only foreground processes are allowed to read from or, if the user so specifies with stty tostop, write to the terminal. Background processes which attempt to read from (write to when stty tostop is in effect) the terminal are sent a SIGTTIN (SIGTTOU) signal by the kernel's terminal driver, which, unless caught, suspends the process.

  • Great, thank you--this cleared up a lot! Jul 27, 2012 at 16:37
  • 3
    Note when not interactive, zsh redirects the standard input of background commands (including those in <(cmd)) to /dev/null, so the behavior differs (zsh -c 'cat <(cat)' returns immediately and doesn't output anything). Nov 3, 2012 at 23:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .