Home > Back-end >  How to move block of text and next nth line following a pattern to end of file?
How to move block of text and next nth line following a pattern to end of file?

Time:09-14

I have this ssh config that needs to be edited.

Host vps6
   HostName 123.456.789.00
   User dylan
   Port 123

Host vps4
   HostName 123.456.789.00 
   User dylan
   Port 123

# old server

Host vps3-old
   HostName 123.456.789.00
   User dylan 
   Port 123

I want to move config for vps6 to end of file and append -old to its config alias. The resulting file would be.

Host vps4
   HostName 123.456.789.00 
   User dylan
   Port 123

# old server

Host vps3-old
   HostName 123.456.789.00
   User dylan 
   Port 123

Host vps6-old
   HostName 123.456.789.00
   User dylan
   Port 123

I managed to do exactly that using this sed command → sed '/'"vps4"'/{N;N;N;N;H;$!d}; ${p;x;s/'"vps4"'/'"vps4"'-old/}', unfortunately this gives me unwanted newline at the end of file.

[tmp]$ sed '/'"vps6"'/{N;N;N;N;H;$!d}; ${p;x;s/'"vps6"'/'"vps6"'-old/}' config
Host vps4
   HostName 123.456.789.00
   User dylan
   Port 123

# old server

Host vps3-old
   HostName 123.456.789.00
   User dylan 
   Port 123

Host vps6-old
   HostName 123.456.789.00 
   User dylan
   Port 123

[tmp]$ # See above me

Moreover, I want to be able to specify the next n line to be moved (for example above will be mark Host vps4 and next 3 line to be moved to end of file). I have searched up the net and found out that the recommended tools for this kind of task is ed, but I have yet to find out the example command to do exactly what I want.

CodePudding user response:

With your shown samples please try following awk code.

awk '
!NF && found{
  found=""
  next
}
/^Host vps6/{
  found=1
  line=$0"-old"
  next
}
found{
  val=(val?val ORS:"")$0
  next
}
!found
END{
  print ORS line ORS val
}
'  Input_file

NOTE: In case you want to save output into Input_file itself then run above program it will print output on terminal and once you are Happy with results of above program then you can append > temp && mv temp Input_file to above program, to do inplace save into Input_file.

Explanation: Adding detailed explanation for above used code.

awk '                      ##Starting awk program from here.
!NF && found{              ##Checking if line is empty AND found is SET then do following.
  found=""                 ##Nullifying found here.
  next                     ##next will skip all further statements from here.
}
/^Host vps6/{              ##If line starts from Host vps6 then do following.
  found=1                  ##Setting found here.
  line=$0"-old"
  next
}
found{                     ##If found is set then do following.
  val=(val?val ORS:"") $0  ##Creating val which is keep adding current line into it.
  next                     ##next will skip all further statements from here.
}
!found                     ##If found is NOT set then print that line.
END{                       ##Starting END block of this program from here.
  print ORS line ORS val   ##Printing ORS line ORS and val here.
}
'  Input_file              ##Mentioning Input_file name here. 

CodePudding user response:

Another awk:

$ awk -v RS= '{                   # read blank line separated records
    if(/^Host vps6/) {            # buffer matching record
        sub(/vps6/,"vps6-old")    # add -old
        b=$0
    } else                        # print non-matching
        print $0 ORS              # with with extra newline
}
END {                             # in the end
    print b                       # output buffer
}' file

if [you] wanted to use variable instead of hardcoded string, use something like this:

$ awk -v s=vps6 -v RS= '{         # or -v s="$sshname"
    if($0~"^Host " s) {           
        match($0,s)
        b=substr($0,1,RSTART RLENGTH-1) "-old" substr($0,RSTART RLENGTH)
    } else
        print $0 ORS
}
END {
    print b
}

CodePudding user response:

Edit: this is basically the same as @RavinderSingh13's answer, but not as good.


Another potential option:

cat file1
Host vps6
   HostName 123.456.789.00
   User dylan
   Port 123

Host vps4
   HostName 123.456.789.00
   User dylan
   Port 123

# old server

Host vps3-old
   HostName 123.456.789.00
   User dylan
   Port 123
awk '
{flag=0}
/vps6/,/^$/ {flag=1}
{
    gsub("vps6", "vps6-old")
    if (flag == 0) print
    if (flag == 1) a[NR]=$0
}
END {
    print ""
    for (i in a) {
        if(a[i] != "") print a[i]
    }
}' file1

Host vps4
   HostName 123.456.789.00
   User dylan
   Port 123

# old server

Host vps3-old
   HostName 123.456.789.00
   User dylan
   Port 123

Host vps6-old
   HostName 123.456.789.00
   User dylan
   Port 123

CodePudding user response:

This might work for you (GNU sed):

sed '/Host vps6/{s//&-old/;:a;N;/\n$/!ba;H;d};$G' file

Match vps6 and append -old.

Gather it up and the following lines until a spaced line, then copy those lines to the hold space (use H rather than h so as to prepend a newline).

Delete those lines.

At the end of the file append the copies.

If the empty line at the end of the file bothers you:

sed '/Host vps6/{s//&-old/;:a;N;s/\n$//;Ta;H;d};$G' file

CodePudding user response:

Here is a gnu-awk solution that won't rely on extra line breaks between each Host record:

awk -v RS='(^|\n)Host ' -v s='vps6' '
$1 == s {
   sub(s, s "-old")
   rec = RT $0
   next
}
{ORS=RT}
1
END {print rec}' file

Host vps4
   HostName 123.456.789.00
   User dylan
   Port 123

# old server

Host vps3-old
   HostName 123.456.789.00
   User dylan
   Port 123

Host vps6-old
   HostName 123.456.789.00
   User dylan
   Port 123
  • Related