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.
cmd x>&y
says: "redirect fd x to whatever resource fd y is redirected to"
So in:
cat 1>&3 3>&1
You're saying that cat
's fd 1 (stdout) should go to the same resource as open on fd 3, that is ./tmp
open in read+write mode; and then fd 3 to the same resource as open on fd 1, which you've just made to be ./tmp
.
So cat
will be started with both its fds 1 and 3 redirected to ./tmp
. cat
doesn't do anything with its fd 3, you can redirect cat
's fd 3 to anything you like, it will have no effect.
However it does write the content of file
to its fd 1, so you'll find the content of file
written at the start of ./tmp
.
cat file
writes the content of file
only once and only to one fd. If you want something that write the content of file
twice to two different fds, you'd want tee
instead:
In
< file tee ./tmp
tee
would write what it reads from its fd 0 (here redirected to file
open in read-only mode) to both ./tmp
(open in write only mode with truncate) and its fd 1 (stdout).
While it's possible to tell tee
to open ./tmp
in append mode with -a
, you can't tell it to open ./tmp
in read+write mode without truncation like 3<>
does.
For that, you'd need:
< file tee /dev/fd/3 3<> ./tmp
which would work except on Linux, or resort to using zsh
and its builtin teeing facility:
cat < file 3<> ./tmp >&1 >&3 3>&-
With fd 1 being redirected twice, zsh
, when the mulltios
option is enabled (on by default), would do an internal tee to forward the output to both destinations (by running an internal process that reads cat
's output and writes it to both destinations).
The temporary use of fd 3 is to work around the the problem that in
cat < file >&1 1<> ./tmp
zsh would complain with zsh: file mode mismatch on fd 1
as it supposes you want to read from ./tmp
.
Best Answer
The spawn command returns a readable stream, so you will need to pipe that to a writable stream to do something useful.
Piping to a file
You will need to install the filed module to run the example above
Piping to stdout and stderr
process.stdout and process.stderr are both writable streams so you can pipe the output of your spawned command directly to the console as well