Bash loops can read the input as words or lines, but what if you needed to accept multi-line input with a single loop iteration?

In the following simple example, we randomly select a hundred lines from /var/log/messages and count the number of characters in each line:

shuf -n 100 /var/log/messages | while read line; do wc -c <<<"${line}"; done

Let’s imagine that we needed to read this input in groups of three lines and calculate the average number of characters per line for each group. You can do this with awk but there is a way to accomplish the task using a Bash loop:

exec 5< <(shuf -n 100 /var/log/messages)
while read line1 <&5
do
  read line2 <&5
  read line3 <&5
  if [ ! -z "${line2}" ] && [ ! -z "${line3}" ]
  then
    printf '%.2f\n' $(echo "($(wc -c <<<"${line1}") + $(wc -c <<<"${line2}") + $(wc -c <<<"${line3}")) / 3" | bc)
  fi
done
exec 5<&-
Sample output
⌂2.64 [root@ncc1701:~] # while read line1 <&5 > do
>   read line2 <&5 >   read line3 <&5 >   if [ ! -z "${line2}" ] && [ ! -z "${line3}" ]
>   then
>     printf "${i},%.2f\n" $(echo "($(wc -c <<<"${line1}") + $(wc -c <<<"${line2}") + $(wc -c <<<"${line3}")) / 3" | bc) >     (( i = i + 1 ))
>   fi
> done | (echo "GROUP,AVERAGE" && cat) | column -s',' -t
GROUP  AVERAGE
1      178.00
2      136.00
3      259.00
4      162.00
5      171.00
6      195.00
7      263.00
8      196.00
9      254.00
10     226.00
11     255.00
12     164.00
13     157.00
14     165.00
15     212.00
16     256.00
17     172.00
18     204.00
19     149.00
20     210.00
21     255.00
22     153.00
23     161.00
24     157.00
25     205.00
26     191.00
27     250.00
28     220.00
29     161.00
30     243.00
31     256.00
32     207.00
33     182.00