What does the following script mean?

exec 4<&0 0</etc/XX/cfg
read line1
exec 0<&4

It redirects fd0 to fd4, and "/etc/XX/cfg" to fd0.

So why does read still work, shouldn't that be empty?


It redirects stdin (FD0) to FD4, redirects from /etc/XX/cfg to FD0, reads a line from FD0, and then moves FD4 back to FD0. In short, it saves, replaces, and restores stdin, while reading a line from a file in between.

read line1 < /etc/XX/cfg would be so much easier, but it's impossible to tell if it's a valid replacement based only on the code shown.

  • 4
    It doesn't "redirect" fd0 to fd4, it "duplicates" it (using the dup2 syscall), so that reading from either descriptor will have the same effect. Of course, it then overwrites fd0, so you can think of it as "moving" the descriptor, if you like. – ams Apr 10 '12 at 10:56
  • So if the stream went from the file to fd0 , from fd0 to fd4 , why would reading from fd0 still work ? – daisy Apr 10 '12 at 10:59
  • It doesn't remove the stream from the old FD. Plus, it opens a file on it regardless. – Ignacio Vazquez-Abrams Apr 10 '12 at 11:00
  • @warl0ck: there's two parts to the first exec. It's only between the two that you can read stdin on both descriptors. Opening the file on fd0 overwrites the old setting, which is why the code saves it on fd4 first. – ams Apr 10 '12 at 11:51

To recast this in syscalls (using C):

exec 4<&0 0</etc/XX/cfg

/* Duplicate fd0 as fd4.  */
dup2 (0, 4);

/* Open file on fd0.
   "open" always uses lowest available descriptor, so we don't need to check it. */
close (0);
open ("/etc/XX/cfg", O_RDONLY);

exec 0<&4

/* Close fd0 and duplicate fd4 as fd0.  */
dup2 (4, 0);

The way I read this - based on the bash manpage section on REDIRECTION - is that stdin (fd0) is redirected into fd4, then input is taken from /etc/XX/cfg into stdin - which will end up on fd4.

read line1 should then take from fd4 which is then put back to fd0.

A demo might show my meaning a little better and answer "why" you might do this. By adding another line and putting it in a wrapper:

$ vim ./test.sh
exec 4<&0 0</etc/XX/cfg
read line1    # reads from fd4
exec 0<&4
read line2    # reads from fd0

echo $line1 
echo $line2

You can pipe or redirect stdin via test.sh but also read in configuration by redirection, so in the code above I've pulled in values from the "config" (assumption on my part based on the filename - bad :) ), but I can process via stdin as well.


$ ./test.sh < somefile
$ cat somefile | ./test.sh

Hopefully this explains it.

  • I think you've misunderstood: there are no reads from fd4 here. – ams Apr 10 '12 at 12:48
  • Having read your comment above, I think you're right, I have. – da-architect Apr 10 '12 at 12:54

protected by Community Apr 28 '15 at 13:49

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.