40

How do I get the last non-empty line using tail under Bash shell?

For example, my_file.txt looks like this:

hello
hola
bonjour
(empty line)
(empty line)

Obviously, if I do tail -n 1 my_file.txt I will get an empty line. In my case I want to get bonjour. How do I do that?

Debugger
  • 9,130
  • 17
  • 45
  • 53

9 Answers9

39

Use tac, so you dont have to read the whole file:

tac FILE |egrep -m 1 .
Jürgen Hötzel
  • 18,997
  • 3
  • 42
  • 58
29

You can use Awk:

awk '/./{line=$0} END{print line}' my_file.txt

This solution has the advantage of using just one tool.

Hai Vu
  • 37,849
  • 11
  • 66
  • 93
  • 9
    Downside: Needs to read the WHOLE file, and assign EACH LINE to awk variables -> This can be quite CPU and IO intensive. – Jürgen Hötzel Apr 16 '10 at 19:40
  • 2
    Yes, the solution is simple, but far from being efficient. An efficient solution would open the file, seek to the end and scan backward. – Hai Vu Apr 16 '10 at 23:24
26

How about using grep to filter out the blank lines first?

$ cat rjh
1
2
3


$ grep "." rjh | tail -1
3
Andreas Louv
  • 46,145
  • 13
  • 104
  • 123
RichieHindle
  • 272,464
  • 47
  • 358
  • 399
  • 7
    or, instead of 'cat rjh | grep "." | tail -1', use 'grep . rjh | tail -1' – amertune Apr 14 '10 at 17:03
  • could someone explain the grep "." part ? – Ab5 Oct 30 '15 at 06:46
  • 1
    In `grep "."`, that period is regex, meaning "any character". In regex a blank line has a beginning (^), an end ($ or \n), but nothing in the middle, so grepping for the period only returns lines that contain characters. E.g., not blank. – Aaron R. Dec 12 '17 at 18:25
  • 1
    Simple > efficient. Bonus: `... | grep \. | tail -1` one char shorter! – hyperpallium Nov 26 '18 at 03:46
6

Instead of tac you can use tail -r if available.

tail -r | grep -m 1 '.'
kalu
  • 61
  • 1
  • 1
  • 1
    Which versions of `tail(1)` have the `-r` option? The one in GNU coreutils doesn't have it, cf. http://debbugs.gnu.org/18808 – Josip Rodin Jul 04 '16 at 10:20
  • The issue with this solution is that if the file you're scanning is a shell script file then it will only filter the echo output rather than the actual file contents, try 1) creating a script file with a few simple echo commands; 2) pasting the full file path into the console and entering " | tail -1" at the end; notice that it will only output your last echo command's output rather than the entire echo command... – 255.tar.xz Jun 03 '19 at 21:58
4

if you want to omit any whitespaces, ie, spaces/tabs at the end of the line, not just empty lines

awk 'NF{p=$0}END{print p}' file
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
2

If tail -r isn't available and you don't have egrep, the following works nicely:

tac $FILE | grep -m 1 '.'

As you can see, it's a combination of two of the previous answers.

Sophie
  • 304
  • 10
  • 12
  • `egrep` is the same as `grep` just instead of needing to put `... | grep "my[regex]Expr"` you only need to put `... | egrep my[regexExpr]` it's a little more complex that that as you may run into a syntax issue, however a few escape characters should do the trick. – 255.tar.xz Jun 03 '19 at 22:01
2

I had problems using other solutions, so I made this.

First, get last 25 lines, assuming at least 1 is not empty. Filter out empty lines, and print out the last line.

 tail -n25 file.txt | grep -v "^.$" | tail -n 1

One major advantage this has, you can show more than 1 line, just by changing the last 1 to, lets say, 5. Also, it only reads last 25 lines of the file.

If you have huge amounts of empty lines, you might want to change the 25 to something bigger, repeating until it works.

pormulsys
  • 21
  • 4
0

Print the last non-empty line that does not contain only tabs and spaces like this:

tac my_file.txt | grep -m 1 '[^[:blank:]]'

Note that Grep supports POSIX character class [:blank:] even if it is not documented in its manual page until 2020-01-01.

File may contain other non-visible characters, so maybe using [:space:] may be better in some cases. All space is not covered even by that, see here.

jarno
  • 787
  • 10
  • 21
  • 1
    If you want to add more lines, change the number after `-m` and add `|tac` to the end to get it back in correct order – Eric Wolf Dec 11 '20 at 23:04
0

Using ag way:

cat file.txt | ag "\S" | tail -1
ー PupSoZeyDe ー
  • 1,082
  • 3
  • 14
  • 33