2

I'm going through a bash script, trying to figure out how it works and potentially patching it. The script in question is this cryptroot script from debian responsible for decrypting block devices at boot. Not being completely at home in bash definitely makes it a challenge.

I found this piece of code and I'm not sure what it does.

if [ -r /conf/conf.d/cryptroot ]; then
    while read mapping <&3; do
        setup_mapping "$mapping" 3<&-
    done 3< /conf/conf.d/cryptroot
fi

My guess is that it reads each line in /conf/conf.d/cryptroot and passes it to setup_mapping. But I don't quite understand how, what the significance of <&3, 3&- and 3</conf/conf.d/cryptroot is, and what they do?

When I read lines from a file I usually do something like this:

while read LINE
    do COMMAND
done < FILE

Where the output of FILE is directed to read in the while loop and doing COMMAND until the last line.

I also know a little bit about redirection, as in, I sometimes use it to redirect STDOUT and STDERR to stuff like /dev/null for example. But I'm not sure what the redirection to 3 means.

After reading a little bit more about I/O redirection I've something close to an answer, according to tldp.org.

The file descriptors for stdin, stdout, and stderr are 0, 1, and 2, respectively. For opening additional files, there remain descriptors 3 to 9.

So the 3 is "just" a reference to an open file or:

...simply a number that the operating system assigns to an open file to keep track of it. Consider it a simplified type of file pointer.

So from what I understand:

  • The 3< /conf/conf.d/cryptroot opens /conf/conf.d/cryptroot for reading and assigns it to file descriptor 3.
  • The read mapping <&3 seems to be reading the first line from file descriptor 3, which points to the open file /conf/conf.d/cryptroot.
  • The setup_mapping "$mapping" 3<&- seems to be closing file descriptor 3, does this mean that it is opened again for every turn in the loop and pointing to the next line?

If the above is correct my question is why do this rather than the "normal" way? e.g.

while read mapping; do
    setup_mapping "$mapping"
done < /conf/conf.d/cryptroot

What advantage (if any) does the first version provide?

Christian Eriksson
  • 2,038
  • 2
  • 22
  • 28
  • 2
    Re: "The `setup_mapping "$mapping" 3<&-` seems to be closing file descriptor 3, does this mean that it is opened again for every turn in the loop and pointing to the next line?": To run a program, a Unix-y shells has to fork off a child process. That child process gets a copy of all file descriptors. The `3<&-` only closes the child process's copy of `3`. The shell process running the loop still has the original `3`, pointing to the right place in the file. – ruakh Aug 26 '19 at 23:16

1 Answers1

5

A common problem with

while read LINE
    do COMMAND
done < FILE

is that people forget the COMMAND is also reading from FILE, and potentially consuming data intended to be read by read in the control of the while loop. To avoid this, a common idiom is to instead read from a different file descriptor. That is accomplished with <&3. But doing that opens file descriptor 3 for COMMAND. That may not be an issue, but it's reasonable to explicitly close it with 3<&- . In short, the construction you're seeing is just a way to avoid having setup_mapping inadvertently read data intended for read.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • 1
    See [this question](https://stackoverflow.com/questions/13800225/shell-script-while-read-line-loop-stops-after-the-first-line) (and the many duplicates listed in the "Linked" sidebar) for examples of the problem this avoids. – Gordon Davisson Aug 27 '19 at 04:06