tail -n1 on latest non-empty line

0

I wanted to achieve something similar to this

grep -v '^$' <myfile-with-blank-lines-at-the-end> | tail -n 1

Basically, I have a file with a unique number, followed by a comma and I want to grab the last id, even when there might be empty lines underneath. The goal is to use just tail

oneindelijk

Posted 2020-01-21T22:29:37.840

Reputation: 207

Is there something wrong with this grep+tail solution? Why do you want just tail? – Kamil Maciorowski – 2020-01-21T23:18:08.093

No, nothing wrong, just wondering if there is a more concise solution – oneindelijk – 2020-01-22T10:23:06.307

Answers

1

The goal is to use just tail.

There is no way.


Analysis

The two tools you used were created with Unix philosophy in mind:

  • Write programs that do one thing and do it well.
  • Write programs to work together.
  • Write programs to handle text streams, because that is a universal interface.

In your example they work together, passing a text stream one to another. Each does its "one thing":

These things are orthogonal; there is no common component.

tail doesn't care of the content, in particular it doesn't care if any line is empty. In effect you cannot do what you want with sole tail. If anyone expanded tail to handle your case, I would say it's the Wrong Thing.

grep doesn't care if it has reached the last line. There is the \Z anchor designed to match "at the end of the string as well as before the final line break in the string (if any)" or even "at the end of the string as well as before all trailing line breaks in the string (if any)" (depending on flavor, see this). The latter would be useful to your case, but grep doesn't seem to support \Z (or anything similar) at all.


More general tools

There are tools whose "one things" are broad enough to cover the two tasks. You can certainly use a general purpose text processing tool like sed or awk.

Indeed, each of the following commands does (almost) what your original command does:

sed -n '/./ h; $ {g;p}'
awk '{if ($0 != "") buffer=$0} END {print buffer}'

Almost, because if there is no non-empty line (which also includes the case of totally empty input), there will be one empty line in the output. Your original command produces nothing in such case. We need some logic to really mimic your command. Like this:

sed -n '/./ h; $ {g;s/^$//;t;g;p}'
awk '{if ($0 != "") buffer=$0} END {if (buffer != "") print buffer}'

Even without this additional logic I find both solutions less concise than your grep … | tail …. No surprise here. Very simple scripts (or/and default options) for a general tool will probably do nothing interesting; while a good specialized tool is designed to do its most common task with quite simple syntax.

This means you shouldn't expect to find a single general tool that allows you to do what you want as easily as grep … | tail …. If the pipeline was long and complex then sed or awk might be the best approach, a simplifying one. But your use case is "search for lines (not) matching a pattern and pick the last one". This is exactly what grep and tail are for.


KISS

Keep it simple and straightforward. Your original command is very straightforward. One needs only some basic knowledge of the two tools to roughly understand what's going on. I see grep … | tail … and I immediately can say we're searching for some pattern and going to pick few last lines. Because this is what these tools do, respectively.

I see awk … or sed … and it can be anything. Even if some (esoteric? golfing?) language allows us to use more concise code for your particular task, I would still opt for grep … | tail …, especially if the code is going to last and be maintained.


Improvement

I'm able to simplify your original command a bit, but its form of grep … | tail … stands:

# original, for comparison, commented out
# grep -v '^$' | tail -n 1

# simplified
grep . | tail -n 1

Kamil Maciorowski

Posted 2020-01-21T22:29:37.840

Reputation: 38 429

That is enlightening !
Funny I forgot about

  • Write programs that do one thing and do it well.

Which is a quote I sometimes use even outside the scope of Linux or even IT (in life !) – oneindelijk – 2020-02-26T15:22:07.693