简体   繁体   中英

sort rows starting with pattern using awk or sed

I'd like to print router configuration and sort only lines starting with pattern crypto isakmp key 6 .

The important thing is that I want to leave that lines in same place so all lines before and after these lines should stay in same place and order (not sorted).

Example input file:

123 345
678 901
bla bla bla
ble ble ble
crypto isakmp key 6 kokofeofepokpfowkfpwjeiofjwiojefiow address 123.456.789.012
crypto isakmp key 6 ofjwiojefiow352okdwofkwkfi9i42kpfsej09f09j4 address 123.456.789.012
crypto isakmp key 6 9i42kpfsej09f09j4ofjwiojefiow352okdwofkwkfi address 123.456.789.012
crypto isakmp key 6 9j4ofjwiojefiow352okdwofkwkfi9i42kpfsej09f0 address 123.456.789.012
ccc ddd eee
fff ggg hhh iii
123 456

So first I'd like to print unchanged ( random count of rows ):

123 345
678 901
bla bla bla
ble ble ble

Then I want to print SORTED lines starting with crypto isakmp key 6.

At the end I'd like to print rest of file unchanged (also random count of rows):

ccc ddd eee
fff ggg hhh iii
123 456

I've managed this by many operations including getting first and last position of crypto isakmp key 6 and using tail / head commands but it's quite complicated and I wonder if there is option in AWK/SED maybe other linux tool to manage it for specified lines. Please explain what your command does in steps.

Expected output (crypto sorted rest intact):

123 345
678 901
bla bla bla
ble ble ble
crypto isakmp key 6 9i42kpfsej09f09j4ofjwiojefiow352okdwofkwkfi address 123.456.789.012
crypto isakmp key 6 9j4ofjwiojefiow352okdwofkwkfi9i42kpfsej09f0 address 123.456.789.012
crypto isakmp key 6 kokofeofepokpfowkfpwjeiofjwiojefiow address 123.456.789.012
crypto isakmp key 6 ofjwiojefiow352okdwofkwkfi9i42kpfsej09f09j4 address 123.456.789.012
ccc ddd eee
fff ggg hhh iii
123 456

Don't fully understand what you mean by sorted but this will sort the crypto lines alphabetically and leave the others as they are

Required GNU awk for the asort function.

awk 'y=/crypto isakmp key 6/{x=1;a[NR]=$0}
     x&&!y{x=asort(a);for(i=1;i<=x;i++)print a[i];x=0};!x' file

123 345
678 901
bla bla bla
ble ble ble
crypto isakmp key 6 9i42kpfsej09f09j4ofjwiojefiow352okdwofkwkfi address 123.456.789.012
crypto isakmp key 6 9j4ofjwiojefiow352okdwofkwkfi9i42kpfsej09f0 address 123.456.789.012
crypto isakmp key 6 kokofeofepokpfowkfpwjeiofjwiojefiow address 123.456.789.012
crypto isakmp key 6 ofjwiojefiow352okdwofkwkfi9i42kpfsej09f09j4 address 123.456.789.012
ccc ddd eee
fff ggg hhh iii
123 456

Explanation

y=/crypto isakmp key 6/
 #variable y is set to 1 if the line contains this regex, 0 if not
{
 #The following code block within the brackets is executed if y is non zero
x=1
 #Set x to 1(i.e true),done every match because it is less hassle and has no negative 
 #side effects 
a[NR]=$0
 #Create array element in array a with a key of NR(line number,doesn't actually matter what 
 #it is though just has to be unique each line) and a value of $0(the line)
}
 #End that block
x&&!y
 #If x(set in the previous block to 1) is set and y isn't (meaning we have encountered a 
 #crypto line but the one we are currently on isn't a crypto line) then
{
 #Open block like before
x=asort(a)
 #Sort the array a, and set x to the number of elements
for(i=1;i<=x;i++)
 #for each element
print a[i]
 #Print the element , note the loop ends here as we have not enclosed in brackets
x=0
 #Set x to 0(false)
}
 #End block
!x
 #Default action for awk is to print the line if an command returns true, so will print any 
 #line where x is not set or is 0 i.e not crypto lines. We could have also used y'

With meaningful names

awk 'InBlock=/crypto isakmp key 6/{Stored=1;Lines[NR]=$0}
     Stored&&!InBlock{
         Count=asort(Lines)
         for(i=1;i<=Count;i++)print Lines[i]
         Stored=0
     }
     !InBlock' file

Here is what I did:

# get interesting lines with numbers
LINER1=`grep -n "^crypto isakmp key 6" r1`

# get interesting lines without numbers for later output
LINER1F=`grep "^crypto isakmp key 6" r1`

# get whole config rows count
LENGTHR1=`wc -l r1|awk '{print $1}'`

# get 1st interesting line number
STARTR1=`echo "$LINER1" | head -1 | cut -f 1 -d:`

# get last interesting line number    
ENDR1=`echo "$LINER1" | tail -1 | cut -f 1 -d:`

# assign 1st segment to variable
SEGMENT1R1=`head -n $(( $STARTR1 - 1 )) r1`

# assign interesting sorted segment to next variable
SEGMENT2R1=`echo "$LINER1F"|sort`

# assign last segment to variable
SEGMENT3R1=`tail -n $(( $LENGTHR1 - $ENDR1 )) r1`

# output whole config with sorted segment to file
echo "$SEGMENT1R1" > r1
echo "$SEGMENT2R1" >> r1
echo "$SEGMENT3R1" >> r1

I hope this might be done simple way without so many steps.

You don't tell us what you want to sort on, or how you want it sorted, or show us the expected output so this is a guess but maybe it is or is close to what you want:

$ cat tst.awk
/crypto isakmp key 6/ {
    buf[$0]
    gotBuf = 1
    next
}
gotBuf {
    PROCINFO["sorted_in"] = "@ind_str_asc"
    for (line in buf) {
        print line
    }
    gotBuf = 0
}
{ print }

$ awk -f tst.awk file
123 345
678 901
bla bla bla
ble ble ble
crypto isakmp key 6 9i42kpfsej09f09j4ofjwiojefiow352okdwofkwkfi address 123.456.789.012
crypto isakmp key 6 9j4ofjwiojefiow352okdwofkwkfi9i42kpfsej09f0 address 123.456.789.012
crypto isakmp key 6 kokofeofepokpfowkfpwjeiofjwiojefiow address 123.456.789.012
crypto isakmp key 6 ofjwiojefiow352okdwofkwkfi9i42kpfsej09f09j4 address 123.456.789.012
ccc ddd eee
fff ggg hhh iii
123 456

The above used GNU awk 4.* for sorted_in.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM