2

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
yivi
  • 42,438
  • 18
  • 116
  • 138
mike
  • 275
  • 1
  • 12

3 Answers3

3

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
123
  • 10,778
  • 2
  • 22
  • 45
  • Seems it's working. Btw. there is an error in my and Your regex i think it should be with "^crypto isakmp key 6" because it should match only starting lines. Could You explain step by step what is that command doing with explaining !y !x && etc... :)? Thanks. – mike Nov 25 '15 at 10:57
  • @mike As you made no attempt to answer the question yourself, and the fact that those are extremely basic commands, I'll let you work out what they do for yourself. If you have any problems with specific parts of the script after looking up basic awk syntax then i will explain them. – 123 Nov 25 '15 at 11:02
  • I posted answer with question but I asked for more compact version using AWK or SED. This indeed works but I don't understand why. That's why I asked If You can explain how it works. I understand that it creates array with variables NR current line which has regexp and then it stores that line contents $0 and finally put all of that to y? Don't know why there is x=1 for every regexp hit. Later You create logical && between "x" which is currently what? and negation of y which is connected to "sort engine". At the end there is printed negation of x.I still don't understand all steps :/. – mike Nov 25 '15 at 13:33
  • 2
    I suspect if you used variable names with a couple of extra characters the OP might not be so confused. Single char variable names like `i` are fine when their meaning is extremely obvious but in a script like this variable names `a`, `x`, and `y` don't provide any information on what they represent. You should mention that this requires GNU awk for `asort()`. – Ed Morton Nov 25 '15 at 14:40
  • @EdMorton I don't think this is long or complicated enough to warrant proper names. I will add a note about GNU awk though. – 123 Nov 25 '15 at 14:53
  • I'm pretty experienced using awk and I can't figure out what x and y represent (given how much effort I'm willing to put into trying). I can tell what effect they have by reading your comments but I don't know what they ARE. I suspect you don't know either and so can't come up with meaningful names for them and that's the real reason for the singe character "names". – Ed Morton Nov 25 '15 at 14:58
  • @EdMorton That is entirely the reason, they don't have meaningful attributes that could be named. They are useful in only the tiny section of code that they are used and so naming them was pointless. If you want me to name them then i will. – 123 Nov 25 '15 at 15:01
  • 1
    I don't care either way, I was just suggesting a way that the script might be modified to be easier for the OP to understand. Using `x` to mean 2 completely different things (a flag set when a condition is true AND the number of elements in the array returned by asort()) also makes the script more confusing than necessary. Then when it's negated for the print at the end I am completely lost about what it is representing at that point. – Ed Morton Nov 25 '15 at 15:03
  • @EdMorton Maybe not the best names, but there you go. Also yes i reused x for different things as it had served it's previous use. – 123 Nov 25 '15 at 15:10
  • 1
    That is a VAST improvement, far more than I expected. The logic is now completely obvious. IMHO you could throw away all those comments as they are now completely unnecessary. wrt reusing x - variable names are cheap, don't overload them - there's absolutely no benefit to doing that and it just obfuscates your code. – Ed Morton Nov 25 '15 at 15:15
  • @123 I'm not sure if I understand it well but. It proceed 1st line and just print it because y does not match regex and x=0 so it does only ; !x which is 1 and means print. It does that 4 times until it match y regex then y gets value 1 and set x to 1 and assign a[5] to contents of 5th line. Then it goes to check x=1 && !y=0 so it's false and skips second block and don't print !x because it's 0. It reply 4 times assigning to a[8]. Next step is y=0 and x=1 so 2nd block is TRUE and it does sort on array a[] and assign x=array length, it prints all sorted lines and set x to 0 so it print rest !x – mike Nov 25 '15 at 19:49
  • @123 I wonder if it's hard to alter this command to do this sort for regex BETWEEN /ble ble ble/ and /ccc ddd eee/ ? :) I think it's totally different method. Great thanks :) good piece of code. – mike Nov 25 '15 at 19:52
  • @mike Thats general gist of it yeah :) Also for between two strings is quite different yes. There are already quite a few answered on here regarding that. If none of them are helpful though you can always open a new question. – 123 Nov 26 '15 at 08:39
  • @123 ok here You are: http://stackoverflow.com/questions/33947274/bash-sort-alphabetically-lines-between-2-patterns – mike Nov 26 '15 at 21:43
0

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.

mike
  • 275
  • 1
  • 12
  • you have the logic well mapped out. Convert that to `awk` code, but list the file twice on cmd line, the first time will calc all your variables, the 2nd time to print it out in your changed order. depending on your version of awk, sorting may be the tricky bit. You can use `if (FNR==NR){first time thru} else {2nd time}` to control what processing is happening. Good luck. – shellter Nov 25 '15 at 10:36
0

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.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • Just simple sort on crypto lines ( without additional conditions ), I've attached output. – mike Nov 25 '15 at 14:24
  • There is no such thing as "simple sort" - everything is sorted on something using some criteria and in some kind of order. It sounds like you want to sort on the whole line and I'm guessing you want a case-sensitive alphabetic sort in ascending order, right? Ive updated my answer to do that. – Ed Morton Nov 25 '15 at 14:27
  • Yes alphabetic sort in ascending order :) - anyway I need to sort 2 files so it matches one to another, any sort will be ok in this scenario because it will sort both files in the same way. I need this to compare that files. – mike Nov 25 '15 at 15:12
  • Hmm, if that's the goal then I suspect the sort (and this whole script) is completely unnecessary since you should just use awk on the 2 files, and while parsing the 2nd file test for presence in a hash table created while parsing the first file. – Ed Morton Nov 25 '15 at 15:18