Search code examples
bashfilefopen

Reading from a file in bash without using the standard loop - non-typical use case


I am familiar with the following code to read every line of a file and perform some command.

while read -r line; do
  echo "$line"
done < filename

The above loop is driven by each line in the file with a block of operations performed in the loop.

I have a script with nested loops that spawn and control threads. I want to be able to pull a line at a time from the file by command in the loop instead of having the line frame the loop. For example, in other languages, there would be an fopen() then an fread() and an fclose() toward the end of the program to clean up. Is there an equivalent in bash?

I'm using Ubuntu 18.04 LTS and bash 4.4.20


Solution

  • In bash, you can open a file using the 'exec' command. Assuming no other input files, you can redirect stdin. If stdin is used for something else, consider opening the file on a different file descriptor (3, 4, ...). You can not use 0, 1 and 2 and they are already associated with stdin/stdout/stderr.

    Using stdin:

    exec < filename
    
    read ...
    if [ ... ] ; then
        read ...
    fi
    
    # Close
    exec <&-
    

    Using a different file descriptor

    exec 3<filename
    
    read -u3 ...
    if [ ... ] ; then
        read -u3 ...
    fi
    exec 3<&-
    

    Note that unlike other environment, the code has to choose a distinct file descriptor to use, which can be tricky if there are many files to open at the same time. Better solution, based on @wjandrea comment

    exec {myfile}<filename
    
    read -u$myfile ...
    if [ ... ] ; then
        read -u$myfile ...
    fi
    exec $myfile<&-