Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding sed hold-space work-flow

Tags:

sed

I would like to print out the last line of a file which contains one or more integers. "Hippo 9991" in example below. I tried to achieve this with gsed -n -r '/[0-9]+/h;x;$p' command, but this doesn't quite work:

$ cat testfile 
dog
lion 34
elephant
tiger 7
hippo 9991
zebra
gepard
cat
$ cat testfile | gsed -n -r '/[0-9]+/h;x;$p'
gepard
$ 

Could somebody explain what exactly gsed -n -r '/[0-9]+/h;x;$p' does? As I understand, it should remove the trailing new-line character from line and read the line into pattern space. Then if the line in pattern space contains one or more integers, the line is put into hold space by replacing the previous data in hold space. This cycle is repeated until the last line which will be printed. Obviously I do not understand this correctly. More than a correct answer I would like to understand the work-flow of sed.

like image 843
Martin Avatar asked Dec 09 '22 07:12

Martin


2 Answers

You almost have it. Here is what your script does:

/[0-9]+/h     # if line contains a number, save the line to hold space
x             # swap content of pattern space and hold space
$p            # when on the last line print pattern space

You save the line to hold space then swap it back to pattern space. The contents of pattern space and hold space can be illustrated like this:

Line      Command     Pattern Space       Hold Space
~~~~    ~~~~~~~~~~~   ~~~~~~~~~~~~~       ~~~~~~~~~~
 1       /[0-9]+/h     dog                   
 1           x                             dog                 
 2       /[0-9]+/h     lion 34             lion 34
 2           x         lion 34             lion 34
 3       /[0-9]+/h     elephant            lion 34
 3           x         lion 34             elephant
 4       /[0-9]+/h     tiger 7             tiger 7
 4           x         tiger 7             tiger 7
 .
 .
 .
 $       /[0-9]+/h     cat                 geopard
 $           x         geopard             cat
 $           p         geopard             cat

What you really want is to only swap contents when the last line of the input file is reached. You can do this by grouping the x and p commands:

gsed -n -r '/[0-9]+/h; $ {x;p}' testfile

Output:

hippo 9991

The corresponding pattern space and hold space sequence is now:

Line      Command     Pattern Space       Hold Space
~~~~    ~~~~~~~~~~~   ~~~~~~~~~~~~~       ~~~~~~~~~~
 1       /[0-9]+/h     dog                   
 2       /[0-9]+/h     lion 34             lion 34
 3       /[0-9]+/h     elephant            lion 34
 4       /[0-9]+/h     tiger 7             tiger 7
 .
 .
 .
 $       /[0-9]+/h     cat                 hippo 9991
 $           x         hippo 9991          cat
 $           p         hippo 9991          cat
like image 163
Thor Avatar answered Jan 22 '23 19:01

Thor


The following works for me:

sed -n -r '/[0-9]+/ {h;x}; ${x;p}'

You want to run both h and x only if the integer is present, in your example, x is run every time. At the end, you don't want to print the last line, but the last stored line, so you have to exchange them once more.

like image 21
choroba Avatar answered Jan 22 '23 19:01

choroba