while IFS= read -r line || [[ $line ]]; do ...; done < /path/to/input
The one-liner is a robust way to write this simpler naive one-liner:
while read line; do ...; done < /path/to/input
For each line in
/path/to/input, store the content of the line in the variable
line, and do something with it.
What can go wrong? Why are all the extra things necessary in the one-liner to make the line by line text processing robust?
IFS= prefix (setting
IFS to empty string for the
read), whitespace at the beginning and at the end of lines will not be stored in
line. Take for example this input:
hello ^^ ^^ spaces!
IFS= prefix, the value of
line would be
"hello" instead of
" hello ". If you want to preserve these spaces, make sure to use the
read would interpret backslash
\ characters as escape symbols. Take for example this input:
first line\ second line
-r flag, the trailing
\ at the end of the first line would be interpreted to escape the line break and thereby continue the input, the value of
line would become
first linesecond line.
|| [[ $line ]], if the last line of the input doesn't have an EOL (end-of-line) character, the loop body will not be reached. This is because
read exits with failure when it reaches EOF (end-of-file), which causes the loop to end.
|| [[ $line ]] takes care of this corner case:
||fails, its right side is executed.
[[ $line ]]checks if
$lineactually has some content.
$linewill be empty, and we want to stop the loop.
$linewill not be empty, and we want to continue the loop. In the following iteration the loop will really end, because we're still at the end of the file, and
$linewill be empty then.
To read the lines in the input exactly as they are, the full one-liner idiom at the top is needed.