Bash – File descriptors and redirect in bash

bashfile-descriptorsio-redirectionprocess-substitution

Can someone explain why this command works:

gpg --enable-special-filenames --verify --batch \
  <(curl -s https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.0.29.tar.bz2.sig) \
  <(curl https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.0.29.tar.bz2)

And this fails with "curl: (23) Failed writing body (0 != 4096)
(23) Failed writing body" error message:

gpg --enable-special-filenames --verify --batch -- '-&3' '-&4' \
  3<(curl -s https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.0.29.tar.bz2.sig) \
  4<(curl https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.0.29.tar.bz2)

Best Answer

Redirections to specific file descriptors use 3< file. Process substitution uses <( ... ). To combine the two you need to use both:

3< <( ... )

The space is important — otherwise it's interpreted as an attempted here document that is terminated with (, but that isn't valid and you'll get a parse error.


Your gpg command line needs to be:

gpg --enable-special-filenames --verify --batch -- '-&3' '-&4' \
    3< <(curl -s https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.0.29.tar.bz2.sig) \
    4< <(curl https://www.gnupg.org/ftp/gcrypt/gnupg/gnupg-2.0.29.tar.bz2)

This redirects the output of the first curl command into descriptor 3, and the second into descriptor 4, in just the same way as if you read into them from a normal file.


The way that <( ... ) works is that it runs the command with its output connected to either a FIFO or an entry under /dev/fd, and then <( ... ) gets replaced in the command line with the path to that file as an ordinary argument, just as though it'd been written there in the first place. The file path can then be used with other constructs, like redirection.

Your original command ends up running gpg ... 3/dev/fd/63 4/dev/fd/64, and then curl finds that nobody is interested in what it's writing and reports that error. I also got an error from gpg itself saying that those files don't exist, although it was buried in the curl output.