Twitter

Some shell tips -- 13 -- tac

This blog is part of a shell tips list I find useful to use on every script -- the whole list can be found here.

Improving in coding with a language also means widening your culture around this area to be able to find an easy way to do something complicated the day this challenge comes. This is why I would like to talk about tac today.

Everyone knows the cat command which prints one or more files to the standard output. tac, thus its name, does the opposite. So no, the opposite of printing a file to the standard output is not UN-printing a file to the standard output nor NOT printing a file to the standard output, it is printing a file to the standard output in reverse.

Let's say we have the below logfile we can print on the standard output using cat:
$ cat logfile
Start of patching 1     <== Start of patching session 1
many things happened here
Exit status:0; End of patching 1

Start of patching 2     <== Start of patching session 2
many things happened here as well; some failed
Exit status:123; End of patching 2
$
With tac we would have:
$ tac logfile
Exit status:123; End of patching 2
many things happened here as well; some failed
Start of patching 2     <== Start of patching session 2

Exit status:0; End of patching 1
many things happened here
Start of patching 1     <== Start of patching session 1
$
See? the output is in reverse. This is when you should be starting asking yourself "ok great but what is the use of that ?".

If we continue with this patching logfile example, we can see that the new patch sessions logs are appended to this logfile and you would be most likely interested by the latest patch session and return code and here is where tac makes it very easy compared to have to get the latest patch logs reading the logfile from the top:
$ tac logfile.log | awk '{print $0; if ($1 ~ /^Start/) {exit}}'
Exit status:123; End of patching 2
many things happened here as well; some failed
Start of patching 2
$
For those unfamiliar with awk, this one prints the lines as they are (print $0) and exits when it finds a line starting with Start (if ($1 ~ /^Start/) {exit}). Easy ! (again, try to think to code the same thing reading the file from the top). The only issue now is that the logs are sent in reverse as a feature of tac but we would like to have them on the correct order -- so just tac the output !
$ tac logfile.log | awk '{print $0; if ($1 ~ /^Start/) {exit}}' | tac
Start of patching 2
many things happened here as well; some failed
Exit status:123; End of patching 2
$

Another point to mention is that tac uses end of line as a default separator but you can change this (with -s or --separator) to reverse some list of strings for example:
$ A=$(echo "10;11;12;13;14;" | tac -s ";")
$ echo $A
14;13;12;11;10;
$
The separator can also be a regexp with -r or --regex (let's say your csv can also have comma as a separator on top of semi-column):
$ A=$(echo "10,11;12,13;14;" | tac -r -s "[,;]")
$ echo $A
14;13;12,11;10,
$
Note that if the last field of your csv like variable has no separator, tac will make it a space:
$ A=$(echo "10,11;12,13;14" | tac -r -s "[,;]")  <== no "," nor ";" after 14
$ echo $A
14 13;12,11;10,  <== tac puts a space after 14
$


tac is part of some lesser known Unix/Linux commands. For sure tac is situational but yet easy and powerful which is exactlty what we want ! If, like me, you have experienced some badly implemented moniroting tools re opening tons of old tickets for old alerts because "oh yes the agent has restarted so it is re scanning the whole logfile", maybe a simple knowledge of tac could have saved tons of hours to people closing these useless tickets after an agent restart just by reading the file from the bottom and at least stop at the first occurence of the error pattern found :)



< Previous shell tip / Next shell tip >

No comments:

Post a Comment

OCI: Datapump between 23ai ADB and 19c ADB using database link

Now that we know how to manually create a 23ai ADB in OCI , that we also know how to create a database link between a 23ai ADB and a 19C AD...