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
cat <&3
does exactly what it's supposed to do, namely read from the file until it reaches the end of the file. When you call it, the file position on the file descriptor is where you last left it, namely, at the end of the file. (There's a single file position, not separate ones for reading and for writing.)cat /proc/$$/fd/3
doesn't do the same thing ascat <&3
: it opens the same file on a different descriptor. Since each file descriptor has its own position, and the position is set to 0 when opening a file for reading, this command prints the whole file and doesn't affect the script.If you want to read back what you wrote, you need to either reopen the file or rewind the file descriptor (i.e. set its position to 0). There's no built-in way to do either in a POSIX shell nor in most sh implementations (there is one in ksh93). There is only one utility that can seek:
dd
, but it can only seek forward. (There are other utilities that may skip forward but that doesn't help.)I think the only portable solution is to remember the file name and open it as many times as necessary. Note that if the file isn't a regular file, you might not be able to seek backwards anyway.