Shell – Reading file descriptors

file-descriptorsio-redirectionshell

Content of a file named file

testing 1
testing 2

Command tested

exec 3<> ./tmp
cat file 1>&3 3>&1

Outputs nothing. Why?

I read my command as

cat content in file, then read STDOUT and give it to file descriptor 3 and then take the content from file descriptor 3 and give it's content to STDOUT

Am I reading my command wrong ?

How shall I read my command in order to understand it better and get the output of file


After understanding your answer Stéphane, I tried the following commands

cat file 1>&3 3> ofile

which didn't write the contents of fd 3 (which gets it's content from fd 1 i.e STDOUT) to ofile

but the I tried the following command

cat file 1>&3 && cat <3

which printed the content of fd 3 to the STDOUT.

Why didn't fd 3 write to ofile when fd 3 has the content of fd 1 ?


Stéphane, the command Jesse_b mentioned in the comments below i.e

cat file 3> ofile 1>&3 

works and does write the content of file to ofile but the command

cat file 1>&3 3> ofile

as I mentioned earlier fails to write to ofile.

How did the placement of 1>&3 at the end and at the beginning of 3> ofile affect the output of these two commands ?

Best Answer

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.