Bash – vipe misbehaving on bash process substitution

bashio-redirectionvim

I have the following simple command:

$ echo <(vipe)

I expected it to open a vim buffer, which I can edit, then when I'm done, echo the name of the temporary file assigned to hold the vim buffer contents.

Instead, the vim buffer behaves very strangely, doesn't respond to some keystrokes, and eventually vim crashes completely.

Which of my assumptions is wrong?

Best Answer

First, let's look at what we see in the terminal:

$ echo <(vim)
/dev/fd/63
$ Vim: Warning: Output is not to a terminal

Notice that you get a prompt back immediately, without waiting for the editor to terminate. Process substitution doesn't wait for the command to finish, it creates a pipe between the command and the shell. A name for that pipe is passed to the command that the process substitution is an argument of, here echo which prints out that name and returns immediately, after which bash closes the pipe.

Vim is still running, and its standard input and standard error are connected to the terminal, but not its standard output. So Vim is getting input and emitting a few error messages, but most of its display goes to stdout, which is a broken pipe.

Both bash and vim are reading from the terminal. This causes your keystrokes to be sent somewhat randomly to one or the other. This situation doesn't normally happen because background processes are forbidden from reading from the terminal — if they attempt to do that then they receive a SIGTTIN signal, and if they ignore the signal then their read call returns an error. But in this case, vim is executed as part of the foreground process group, because process substitution does not create a new process group, hence it can read from the terminal and grab some of the keystrokes.

At some point Vim decides that it can't take it anymore and quits. I don't know what makes it decide this.


Process substitution doesn't create a temporary file, it creates a pipe, so your approach doesn't make sense. Zsh has a process substitution construct that does create a temporary file (=(somecommand)), but that wouldn't work either: you can run echo =(vipe </dev/null), and that will print out the name of a temporary file which, at the time it is printed, has just been deleted.

To do what you want to do, you need to handle the deletion of the temporary file by yourself. I don't think there's any shell construct that can help you there. Call mktemp to create the temporary file, and then edit it in the ordinary way.

tmpfile=$(mktemp)
"${VISUAL:-"${EDITOR:-vi}"}" "$tmpfile"
echo "$tmpfile"
…
rm "$tmpfile"
Related Question