Bash – Significance of arrows symbols in duplicating/closing file descriptors under bash

bashfile-descriptorsio-redirection

I'm reading a book about Linux command line where author doesn't seem to follow the conventions in bash manual regarding arrows symbols used in redirection operations. Namely, he always uses left arrow < in duplicating and closing file descriptors regardless of whether the descriptors are input or output ones.

Here is an example:

exec 3<&0 4<&1 #shouldn't be 4>&1 ?
#...
exec 3<&- 4<&- #shouldn't be 4>&- ?

Bash man page is vague in this point, according to it, the duplicating/closing and moving file descriptors have the following syntaxes:

#Duplicating and closing (in case word expands to -):
[n]<&word 
[n]>&word

#Moving:
[n]<&digit-
[n]>&digit-

They are described to have different behaviour only if we don't explicitly supply the n. But when we do, does it mean that we can use these forms interchangeably?

Best Answer

It doesn't matter because both 4>&1 and 4<&1 do the same thing: dup2(1, 4) which is the system call to duplicate a fd onto another. The duplicated fd automatically inherits the I/O direction of the original fd. (same for 4>&- vs 4<&- which both resolve to close(4), and 4>&1- which is the dup2(1, 4) followed by close(1)).

However, the 4<&1 syntax is confusing unless for some reason the fd 1 was explicitly open for reading (which would be even more confusing), so in my mind should be avoided.

The duplicated fd shares the same open file description which means they share the same offset in the file (for those file types where it makes sense) and same associated flags (I/O redirection/opening mode, O_APPEND and so on).

On Linux, there's another way to duplicate a fd (which is not really a duplication) and create a new open file description for the same resource but with possibly different flags.

exec 3> /dev/fd/4

While on Solaris and probably most other Unices, that is more or less equivalent to dup2(4, 3), on Linux, that opens the same resource as that pointed to by the fd 4 from scratch.

That is an important difference, because for instance, for a regular file, the offset of fd 3 will be 0 (the beginning of the file) and the file will be truncated (which is why for instance on Linux you need to write tee -a /dev/stderr instead of tee /dev/stderr).

And the I/O mode can be different.

Interestingly, if fd 4 pointed to the reading end of a pipe, then fd 3 now points to the writing end (/dev/fd/3 behaves like a named pipe):

$ echo a+a | { echo a-a > /dev/fd/0; tr a b; }
b+b
b-b

$ echo a+a | { echo a-a >&0; tr a b; }
bash: echo: write error: Bad file descriptor
b+b