41

I am having a very hard time understanding how does one use file descriptors in shell scripts.

I know the basics such as

exec 5 > /tmp/foo

So fd 5 is attached to foo for writing.

exec 6 < /tmp/bar

… for reading.

exec 5>&-

… close fd.

Now what does this do?

#!/bin/bash

exec 5 > /tmp/foo 
exec 6 < /tmp/bar 

cat <&6 | while read a
do
     echo $a >&5
done

As i understand &5 closes the fd, so how is the output still being re-directed successfully after each call?

This is a copy pasta from : Here

It claims using this over a simple echo $a > file would make it much quicker, I however fail to understand. I would appreciate any links to decent tutorial. I google powers seem to be failing me.

1
  • 5 > and 6 < won't give you what you expect if you leave a space in front of the redirection signs, because > and < alone are by default interpreted as 1> and 0< and will redirect the stdout (fd 1) and stdin (fd 0) respectively, not fd 5 and fd 6 as you might want. The correct syntax is 5> and 6<.
    – The Quark
    Commented Nov 5, 2021 at 9:06

2 Answers 2

61

First, note that the syntax for closing is 5>&- or 6<&-, depending on whether the file descriptor is being read for writing or for reading. There seems to be a typo or formatting glitch in that blog post.

Here's the commented script.

exec 5>/tmp/foo       # open /tmp/foo for writing, on fd 5
exec 6</tmp/bar       # open /tmp/bar for reading, on fd 6
cat <&6 |             # call cat, with its standard input connected to
                      # what is currently fd 6, i.e., /tmp/bar
while read a; do      # 
  echo $a >&5         # write to fd 5, i.e., /tmp/foo
done                  # 

There's no closing here. Because all the inputs and outputs are going to the same place in this simple example, the use of extra file descriptors is not necessary. You could write

cat </tmp/bar |
while read a; do
  echo $a
done >/tmp/foo

Using explicit file descriptors becomes useful when you want to write to multiple files in turn. For example, consider a script that outputs data to a data output file and logging data to a log file and possibly error messages as well. That means three output channels: one for data, one for logs and one for errors. Since there are only two standard descriptors for output, a third is needed. You can call exec to open the output files:

exec >data-file
exec 3>log-file
echo "first line of data"
echo "this is a log line" >&3
…
if something_bad_happens; then echo error message >&2; fi
exec >&-  # close the data output file
echo "output file closed" >&3

The remark about efficiency comes in when you have a redirection in a loop, like this (assume the file is empty to begin with):

while …; do echo $a >>/tmp/bar; done

At each iteration, the program opens /tmp/bar, seeks to the end of the file, appends some data and closes the file. It is more efficient to open the file once and for all:

while …; do echo $a; done >/tmp/bar

When there are multiple redirections happening at different times, calling exec to perform redirections rather than wrapping a block in a redirection becomes useful.

exec >/tmp/bar
while …; do echo $a; done

You'll find several other examples of redirection by browsing the io-redirection tag on this site.

5
  • 3
    Either syntax for closing file descriptor actually does the same thing. Commented Oct 25, 2015 at 12:18
  • Will exec >&- restore stdout (fd 0) to the terminal as initially? If I do exec >file then some commands then exec >&-, after that I get a write error: Bad file descriptor on the following commands.
    – The Quark
    Commented Nov 5, 2021 at 9:27
  • @TheQuark No. exec >&- closes stdout (which is fd 1 by the way). If you want to restore what was open before, you have to save it, and the best way is to let the shell do it for you: instead of ???; exec >file; some commands; ???, do { some commands; } >file Commented Nov 5, 2021 at 10:01
  • Yes, fd 1 (that was a typo). What I mean is that in your answer to the OP there is a exec >&- line, which cannot work, right? Or am I missing something?...
    – The Quark
    Commented Nov 5, 2021 at 10:06
  • 1
    @TheQuark exec >&- closes stdout. It wasn't intended to restore what stdout used to point to. It works in the sense that it does what it's intended to do. And the point including it in this answer was to show examples of exec with redirections. If you want to restore the previous stdout, it isn't the right tool for the job. Commented Nov 5, 2021 at 10:14
0

Each open file gets assigned a file descriptor and the file descriptor for stdin is 0 stdout is 1
stderr is 2 For opening additional files, there remain descriptors 3 to 9.

Take for example

exec 3>&1 4>&2
exec 1>  /proc/1/fd/1 2>&1

3>&1 creates a new file descriptor and redirect it to 1 which is STDOUT 4>&2 also new file descriptor and redirect it to 2 which is STDERR

1> /proc/1/fd/1 uses a socket file descriptor 2>&1 also redirects everything from this file descriptor to 1 which is STDOUT

Ive also learned this pretty recently, so feel free to point out if i am missing something

References for using exec and redirecting using file descriptions https://www.linuxtopia.org/online_books/advanced_bash_scripting_guide/x13082.html

1
  • and redirect it to 1 which is STDOUT Not exactly, it should be and redirect it to wherever 1 (which is STDOUT) *points to*, same for 2. Your wording mixes the source and the destination. Mind though also that stdin, stdout and stderr can be ambiguous as they can designate both the source (output channel of the process) and destination (terminal window, destination file...) depending on the context.
    – The Quark
    Commented Nov 5, 2021 at 9:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .