How do you know you are on the last line when looping through a file?

I have a file called listing.txt
It contains data like:

1
2
3
4
5

I then use the following to loop through the file and do stuff with each line:

IFS=$'n'
while IFS= read -r inc; do
    if [ $inc -eq 1 ]
    then
        echo "FIRST: $inc"
    else
        echo "MID: $inc"
    fi
done </home/user/listing.txt

But I can’t find an option to check if it is the Last line. What’s the best way of finding if I am on the last line?

I’ve seen numerous pages talking about loops, and pages talking about getting the last line from a file, but nothing that helps my loop to know it is on the last line.

Asked By: IGGt

||

What does it mean to be on the last line? It means that there are no lines after this one so one strategy would be to check if there is another line after the current one within the loop. If there isn’t you are on the last line, otherwise you are in the middle.

Another technique would be to keep the line in a variable that is scoped outside the loop, then when the loop ends the variable contains the last line. This has the disadvantage of making the middle calculation incorrect but you could probably find a way around that.

Answered By: user1794469

Reiterating what WAF said in the comments, you could use wc to fetch the number of lines prior the loop and test against it to catch the last line:

IFS=$'n'
last=$(</home/user/listing.txt wc -l)
while IFS= read -r inc; do
    if [ $inc -eq 1 ]
    then
        echo "FIRST: $inc"
    elif [ $inc -eq $last ]
        echo "LAST: $inc"
    else
        echo "MID: $inc"
    fi
done </home/user/listing.txt
Answered By: kos

Get the last line number of the file, also known as the total line count. Loop through the file, keeping track of the current line number. If the current line number equals the last line number, then do something.

#!/bin/bash

file="some-file.txt"
last_line=$(wc -l < $file)
current_line=0

while read -r line; do
  current_line=$(($current_line + 1))

  if [[ $current_line -ne $last_line ]]; then 
    # is NOT last line
  else
    # IS last line
  fi 
done < $file 
Answered By: zwbetz

One option is to ‘unwrap’ part of the loop so that the status of the next line can be used to determine handling for the current line:

{
  IFS= read -r current
  while IFS= read -r next; do
    echo "MID: $current"
    current=$next
  done
  echo "LAST: $current"
} </home/user/listing.txt

A loop like this does not require knowing anything about the line count or the contents of the file. Significantly, it will work with streams of data, i.e. something that is being piped in from another process (see the example below).

To do something distinct with the first line, just unwrap the loop a little bit more:

printf '%sn' a b c d e | {
  IFS= read -r first
  echo "FIRST: $first"
  IFS= read -r current
  while IFS= read -r next; do
    echo "MID: $current"
    current=$next
  done
  echo "LAST: $current"
}

Output:

FIRST: a
MID: b
MID: c
MID: d
LAST: e
Answered By: Gairfowl
Categories: Answers Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.