6

My input file contains data as below

acb/xyz/row<t>
acb/xyz/row<t>
abc/xyz/row<b>
abc/xyz/row<b>
abc/xyz/row<0>
abc/xyz/row<0>
abc/xyz/row<1>
abc/xyz/row<1>
abc/xyz/row<2>
abc/xyz/row<2>
abc/xyz/row<3>
abc/xyz/row<3>
abc/xyz/row<4>
abc/xyz/row<4>

So I want the output to be as shown below:

#Sector Top
acb/xyz/row<t>
acb/xyz/row<t>
#Sector Bottom
abc/xyz/row<b>
abc/xyz/row<b>
#Sector 0
abc/xyz/row<0>
abc/xyz/row<0>
#Sector 1
abc/xyz/row<1>
abc/xyz/row<1>
#Sector 2
abc/xyz/row<2>
abc/xyz/row<2>
#Sector 3
abc/xyz/row<3>
abc/xyz/row<3>
#Sector 4
abc/xyz/row<4>
abc/xyz/row<4>
  • Each sector shown above contains multiple lines, here I have shown only 2 lines per sector.
  • There are top, bottom and from 0 to 30 sectors, here I have shown up to 4 sectors.

I tried for one of the sectors by using sed command as:

sed '/row<1>/i #Sector 1' myfile

which gives me output like:

#Sector 1
abc/xyz/row<1>
#Sector 1
abc/xyz/row<1>

I don't need a new line before every match, only needed before first match for all the sectors.

2
  • 1
    Welcome to the site. Is it guaranteed that the entries are already sorted by sector ID? Can there be empty lines and other comment lines?
    – AdminBee
    Sep 5, 2022 at 8:09
  • Thank you. Yes, the entries are already sorted by ID. No, empty lines are not allowed.
    – Manjunath
    Sep 5, 2022 at 8:43

5 Answers 5

9

Using any awk:

$ cat tst.awk
BEGIN {
    FS = "[<>]"
    map["t"] = "Top"
    map["b"] = "Bottom"
}
{
    sector = $(NF-1)
    if ( sector != prev ) {
        print "#Sector", (sector in map ? map[sector] : sector)
        prev = sector
    }
    print
}

$ awk -f tst.awk file
#Sector Top
acb/xyz/row<t>
acb/xyz/row<t>
#Sector Bottom
abc/xyz/row<b>
abc/xyz/row<b>
#Sector 0
abc/xyz/row<0>
abc/xyz/row<0>
#Sector 1
abc/xyz/row<1>
abc/xyz/row<1>
#Sector 2
abc/xyz/row<2>
abc/xyz/row<2>
#Sector 3
abc/xyz/row<3>
abc/xyz/row<3>
#Sector 4
abc/xyz/row<4>
abc/xyz/row<4>
2
3

You can do this using the N;P;D cycle of sed and only insert the #Sector line if the row number did change:

sed -E 'N;P;/^(.*)\n\1/D;s/.*(\n.*)(row<)([0-9bt])*>/#Sector \3\1\2\3>/;P;D'

Now we need to add handling for the top and bottom section like this:

sed -E -e '1i #Sector top' -e 'N;P;/^(.*)\n\1/D;s/.*(\n.*)(row<)([0-9bt])*>/#Sector \3\1\2\3>/;s/#Sector b/&ottom/;P;D'

Explanation in detail:

  • option -E is to make the output more readable. Instead, you could also add eight backslashes for the (…) sections
  • 1i #Sector top simply adds the first sector header
  • Now we always append the Next line to always process two lines together and Print the first one
  • /^(.*)\n\1/ is an expression that the first line gets repeated, so there is no reason to insert a section header, so we Delete the first line to continue with the second
  • Now some regular expression magic: s/.*(\n.*)(row<)([0-9bt])*>/#Sector \3\1\2\3>/ replaces the first line (already printed!) with the section header and the row number extracted in the third () pair
  • Finally simply change the b to bottom with s/#Sector b/&ottom/
  • and close the cycle by Printing the first line and Deleting it to continue with the second
0
3

Using GNU sed

$ sed -Ez 's/(\S+row<([^>]*)>)(.*\1)?/#Sector \2\n&/g;s/\<t\>/Top/;s/\<b\>/Bottom/' input_file
#Sector Top
acb/xyz/row<t>
acb/xyz/row<t>
#Sector Bottom
abc/xyz/row<b>
abc/xyz/row<b>
#Sector 0
abc/xyz/row<0>
abc/xyz/row<0>
#Sector 1
abc/xyz/row<1>
abc/xyz/row<1>
#Sector 2
abc/xyz/row<2>
abc/xyz/row<2>
#Sector 3
abc/xyz/row<3>
abc/xyz/row<3>
#Sector 4
abc/xyz/row<4>
abc/xyz/row<4>
0
3

You can do this using awk :

awk -F'[<>]' '!seen[$2]++{print "#Sector " $2} {print}' file

and if you need to replace t with Top and b with Bottom you can add some condition :

awk -F'[<>]' '{
    if(!seen[$2]++){
        if($2=="t"){
            print "#Sector Top"
        }else if($2=="b"){
            print "#Sector Bottom"
        }else{
            print "#Sector " $2
        }
    }
    print
}' file

In both case this suppose you don't have more of these < > in your line.

This also suppose each sector are sorted together and that you only have the lines you showed in your data file. If you have an unsorted file it won't insert a new line after the first match.

4
  • {if(!seen[$2]++){print "#Sector " $2 "\n" $0}else{print $0}} could be written as just !seen[$2]++{print "#Sector " $2} {print}
    – Ed Morton
    Sep 6, 2022 at 14:01
  • @EdMorton I was actually trying that out... but it keep crashing my vm and I can't get why so I wasn't gonna post it yet. Sep 6, 2022 at 14:06
  • The problem with the approach of using a seen[] array to hold all keys like that instead of just testing each key value as it changes is it has to hold all of the key values in memory so maybe you were running on a very large file and exceeding a threshold? Seems unlikely (especially since your other code does the same thing in that regard) but idk what else would cause your VM to crash.
    – Ed Morton
    Sep 6, 2022 at 14:08
  • 1
    Yes I realized the probleme of using my approch, reading your answer... And I found out why my VM was crashing, it's totally unrelated. Sep 6, 2022 at 14:23
0

The below Script will work in a bash shell

#!/bin/bash
awk 'BEGIN{ FS="/" }
{a=$3;
    if(a != b)
        print "Sector " substr(a,5,1)"\n" a
    else
        print a;
    b=$3}' myfile.txt

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .