Shell Script – What Does `while read -r line || [[ -n $line ]]` Mean?

read

I found some code for reading input from a file a while ago, I believe from Stack Exchange, that I was able to adapt for my needs:

while read -r line || [[ -n "$line" ]]; do
    if [[ $line != "" ]]
    then
        ((x++));
        echo "$x:  $line"
    <then do something with $line>
    fi
done < "$1"

I'm reviewing my script now & trying to understand what it's doing … I don't understand what this statement is doing:

while read -r line || [[ -n "$line" ]];

I understand that the -r option says that we're reading raw text into line, but I'm confused about the || [[ -n "$line" ]] portion of the statement. Can someone please explain what that is doing?

Best Answer

[[ -n "$line" ]] tests if $line (the variable just read by read) is not empty. It's useful since read returns a success if and only if it sees a newline character before the end-of-file. If the input contains a line fragment without a newline in the end, this test will catch that, and the loop will process that final incomplete line, too. Without the extra test, such an incomplete line would be read into $line, but ignored by the loop.

The cmd1 || cmd2 construct is of course just like the equivalent in C. The second command runs if the first returns a falsy status, and the result is the exit status of the last command that executed.

Compare:

$ printf 'foo\nbar' | ( while read line; do echo "in loop: $line" ; done ; echo "finally: $line" )
in loop: foo
finally: bar

and

$ printf 'foo\nbar' | ( while read line || [[ -n $line ]]; do echo "in loop: $line" ; done ; echo "finally: $line" )
in loop: foo
in loop: bar
finally: 
Related Question