Simply run cat <(node -v)
related: https://github.com/robbyrussell/oh-my-zsh/issues/7972
I can reproduce. It's caused by #24260 except not really - the tcsetattr() call (which libc turns into an ioctl()) simply hangs when node tries to restore the state for fd 0, stdin.
I can't reproduce on Linux so this is with 99% certainty a bug in macOS's implementation of tcsetattr(). TBD if and how to work around that.
edit: libc turns tcsetattr(TCSANOW) into ioctl(TIOCSETA) so this is most likely a kernel bug, not a libc bug.
This minimal C test case shows the exact same behavior:
#include <err.h>
#include <termios.h>
int
main(void)
{
struct termios t;
if (tcgetattr(0, &t))
err(1, "tcgetattr");
if (tcsetattr(0, TCSANOW, &t))
err(1, "tcsetattr");
return 0;
}
$ cc t.c
$ ./a.out
# ok
$ cat <(./a.out)
# hangs
IOW, not a Node.js bug. I'll see if I can devise a workaround.
Okay, I figured it out. The tcsetattr() call makes the kernel suspend node with a SIGTTOU signal.
Apparently cat <(node -v) spawns node as a background job that doesn't own the TTY. On Linux that results in the tcsetattr() call failing with EIO (expected), on macOS it sends a SIGTTOU instead.
A fix is on the way.
FWIW, I can repro on Linux, maybe because I'm using zsh not bash.
Most helpful comment
Okay, I figured it out. The
tcsetattr()call makes the kernel suspend node with aSIGTTOUsignal.Apparently
cat <(node -v)spawns node as a background job that doesn't own the TTY. On Linux that results in thetcsetattr()call failing withEIO(expected), on macOS it sends aSIGTTOUinstead.A fix is on the way.