Is there a way to determine whether the current line is the last line of the input stream?
11 Answers
The special END
pattern will match only after the end of all input. Note that this pattern can't be combined with any other pattern.
More useful is probably the getline
pseudo-function which resets $0
to the next line and return 1, or in case of EOF return 0! Which I think is what you want.
For example:
awk '{ if(getline == 0) { print "Found EOF"} }'
If you are only processing one file, this would be equivalent:
awk 'END { print "Found EOF" }'

- 104,111
- 38
- 209
- 254

- 1,467
- 13
- 14
-
Actually, my first example should read: awk '{ while(getline == 1) {}; print "Found EOF" }' – uriel Oct 29 '09 at 21:45
-
True, forgot to mention that, but if he is reading from a bunch of static files this is unlikely, but then I guess that depends very much on the context of his script. – uriel Oct 29 '09 at 22:45
-
Thanks! If one deals with two files, then one can do `cmd="cat file2"; while(condition) {if(cmd | getline line2 == 0) break;` – tommy.carstensen Jan 27 '15 at 00:48
-
You can't do `awk '{ if(getline == 0) { print "Found EOF"} }'` because then you've just consumed the next line so you'd end up only actually doing `{ if(getline == 0) { print "Found EOF"} }` for every odd-numbered line. Try `seq 4 | awk '{print} { if(getline == 0) { print "Found EOF"} }'` and it'll output 1 then 3 and never print `Found EOF`. Also, `awk 'END { print "Found EOF" }'` doesn't tell you `whether the current line is the last line of the input stream` as the OP asked for, it just prints `Found EOF` after youve processed all the input which is a completely different/easier requirement – Ed Morton Dec 31 '22 at 16:49
These are the only sensible ways to do what you want, in order of best to worst:
awk 'NR==FNR{max++; next} FNR == max { print "Final line:",$0 }' file file
awk -v max="$(wc -l < file)" 'FNR == max { print "Final line:",$0 }' file
awk 'BEGIN{ while ( (getline dummy < ARGV[1]) > 0) max++; close(ARGV[1])} FNR == max { print "Final line:",$0 }' file

- 188,023
- 17
- 78
- 185
You've got two options, both kind of messy.
- Store a copy of every current line in a temp variable, and then use the END block to process it.
- Use the system command to run "wc -l | getline" in the BEGIN block to get the number of lines in the file, and then count up the that value.
You might have to play with #2 a little to get it to run, but it should work. Its been a while since I've done any awk.

- 2,027
- 3
- 21
- 25
-
2Yuck, I really hate that option 2. But option 1 is the way to do it. +1 for option 1. – steveha Nov 02 '09 at 19:54
-
No! Don't do option "1" as that's a waste of resources and don't do option "2" as it's simply the wrong way to use wc in that scenario and introduces all the getline caveats. – Ed Morton Dec 12 '12 at 20:49
-
1If the file were to remain the same line-length, you could do `RECORDS=$(cat myfile | wc -l); awk 'NR == '${RECORDS}' {print "Last Line"}' myfile`. Hey, at the point when you choose AWK, you've already gone way past elegance. :) – Dave Aug 21 '15 at 18:00
Detecting the EOF is not too reliable when multiple files are on the command line. Detecting the start of the file is more reliable.
To do this, the first file is special and we ignore the FNR==1.
After the first file then FNR==1 becomes the end of the previous file. last_filename always has the filename that you are processing.
Do your file processing after the else.
Do your EOF processing inside the else block, AND in the END block.
gawk 'BEGIN{last_filename="";} \
FNR==1{if (last_filename==""){last_filename=FILENAME;} \
else {print "EOF: "last_filename;last_filename=FILENAME;}} \
END{print "END: "last_filename;}' $*
For multiple file sets, the else block executes at EOF for all but the last file. The last file is executed in the END block.
For single file sets, the else block doesn't get executed, and the END block is executed.

- 740
- 6
- 24

- 1,061
- 19
- 26
I'm not even sure how to categorize this "solution"
{
t = lastline
lastline = $0
$0 = t
}
/test/ {
print "line <" $0 "> had a _test_"
}
END {
# now you have "lastline", it can't be processed with the above statements
# ...but you can work with it here
}
The cool thing about this hack is that by assigning to $0
, all the remaining declarative patterns and actions work, one line delayed. You can't get them to work for the END
, even if you put the END
on top, but you do have control on the last line and you haven't done anything else to it.

- 143,651
- 25
- 248
- 329
-
Will this only detect the last line of the last file in the awk input file list? – Amit Naidu Jun 02 '13 at 05:18
To detect the last line of each file in the argument list the following works nicely:
FNR == 1 || EOF {
print "last line (" FILENAME "): " $0
}

- 36,151
- 76
- 250
- 438

- 27
- 1
-
There is no `EOF` in awk, I cant imagine how this got upvotes as it can't possibly work for that and other reasons, e.g. it'll print the first line of each file instead of the last line. – Ed Morton Dec 31 '22 at 16:42
One easy way is to run the file via an intermediate sed
script, that places a 0 on every non last line, and a 1 on the last one.
cat input_file | sed 's/^/0/;$s/0/1/' | awk '{LST=/^1/;$0=substr($0,2)}
... your awk script in which you can use LST to check for the
... last line.'

- 160
- 1
- 3
-
First version using sed that I found that actually works, and in my case I didn't want to use awk at all. – xor_eq Mar 04 '16 at 13:22
Hmm the awk END
variable tells when you have already reached the EOF
. Isn't really much of help to you I guess

- 53,475
- 11
- 111
- 124
-
if there are multiple files, END will execute once at the end of the last file. – rickfoosusa Feb 18 '14 at 19:28
you can try this:
awk 'BEGIN{PFNR=1} FNR==PFNR{PFNR++;next} {print FILENAME,PFNR=2} END{print FILENAME}' file1 file2

- 11
- 1
A portable solution is provided in the gawk user manual, although as mentioned in another answer, gawk itself has BEGINFILE and ENDFILE.

- 740
- 6
- 24