Move few lines after a pattern match to another position (just before another match) in shell

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












0















Giving an example to explain the problem.
I have a file with



AAA

BBB

CCC

DDD

EEE

FFF

ABAB

ACAC

GGG

HHH


I want to shift 2 lines after match ABAB to just before DDD. So, the modified file would look like:



AAA

BBB

CCC

ABAB

ACAC

DDD

EEE

FFF

GGG

HHH


Looking for some cool way to handle this preferably using sed.










share|improve this question
























  • Are there really blank lines between each entry?

    – steeldriver
    Feb 24 at 3:32















0















Giving an example to explain the problem.
I have a file with



AAA

BBB

CCC

DDD

EEE

FFF

ABAB

ACAC

GGG

HHH


I want to shift 2 lines after match ABAB to just before DDD. So, the modified file would look like:



AAA

BBB

CCC

ABAB

ACAC

DDD

EEE

FFF

GGG

HHH


Looking for some cool way to handle this preferably using sed.










share|improve this question
























  • Are there really blank lines between each entry?

    – steeldriver
    Feb 24 at 3:32













0












0








0


0






Giving an example to explain the problem.
I have a file with



AAA

BBB

CCC

DDD

EEE

FFF

ABAB

ACAC

GGG

HHH


I want to shift 2 lines after match ABAB to just before DDD. So, the modified file would look like:



AAA

BBB

CCC

ABAB

ACAC

DDD

EEE

FFF

GGG

HHH


Looking for some cool way to handle this preferably using sed.










share|improve this question
















Giving an example to explain the problem.
I have a file with



AAA

BBB

CCC

DDD

EEE

FFF

ABAB

ACAC

GGG

HHH


I want to shift 2 lines after match ABAB to just before DDD. So, the modified file would look like:



AAA

BBB

CCC

ABAB

ACAC

DDD

EEE

FFF

GGG

HHH


Looking for some cool way to handle this preferably using sed.







text-processing sed






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Feb 24 at 3:22









Sergiy Kolodyazhnyy

10.6k42763




10.6k42763










asked Feb 24 at 3:03









PratapPratap

184




184












  • Are there really blank lines between each entry?

    – steeldriver
    Feb 24 at 3:32

















  • Are there really blank lines between each entry?

    – steeldriver
    Feb 24 at 3:32
















Are there really blank lines between each entry?

– steeldriver
Feb 24 at 3:32





Are there really blank lines between each entry?

– steeldriver
Feb 24 at 3:32










4 Answers
4






active

oldest

votes


















2














NOTE: I'm assuming your data does not have blank lines between each entry; if it does, then you will need to address four lines i.e. change + to +3



With GNU ed:



$ ed -s file <<EOF
/ABAB/,//+m?DDD?-
,p
q
EOF


where




  • /ABAB/,//+ addresses a range of lines from /ABAB/ to the previous match // plus one line


  • m moves the addressed lines to


  • ?DDD?- the previous line matching DDD, minus one line


  • ,p print the whole buffer

As a one-liner,



printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s file


To edit file in place, replace ,pnqn by ,wqn (write and quit).






share|improve this answer























  • Works great! How do we handle this when we have to do this processing on output of a previously processed output? sat if I want to cat file and then do this processing after that, how do I do it?

    – Pratap
    Feb 24 at 7:10











  • @Pratap yes you can - but it's not just a matter of piping the previous command output, since ed reads its commands via standard input. Instead, you have to pass the command as a special "file", preceding it with a ! character e.g. printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s !'cat file'.

    – steeldriver
    Feb 24 at 14:20


















1














You wanted with sed so you can do it as shown:



sed -e '
/DDD/,/ABAB/! b
H;/ABAB/!$!d;;g
s/(n.*)n(.*)/21/
' input.txt


This gets straightforward with the ed editor:



 ed -s input.file - <<eof
/ABAB/m?DDD?-
wq
eof





share|improve this answer
































    0














    Python (single read)



    Reading a file in one and storing what's between the two patterns can be done as so:



    #!/usr/bin/env python3
    import sys

    flag=False
    vals =
    with open(sys.argv[1]) as fd:
    for line in fd:
    if line.strip() == "DDD" or flag:
    # encountered line where we should stop appending values
    if line.strip() == "ABAB":
    flag = False
    # print the line and two others, then move in what was between originally
    print(line.strip())
    for i in range(2):
    print(fd.readline().strip())
    print("n".join(vals))
    continue
    # store values while we haven't hit ABAB
    flag = True
    vals.append(line.strip())
    continue

    print(line.strip())


    Python ( double read)



    Re-using original awk idea I had to read the file twice, we can do the same in Python:



    #!/usr/bin/env python3
    import sys


    flag_pos,match = 0,0
    vals =
    with open(sys.argv[1]) as fd:
    for index, line in enumerate(fd):
    if line.strip() == "DDD":
    flag_pos = index
    if line.strip() == "ABAB":
    vals.append(line.strip())
    fd.readline()
    vals.append(fd.readline().strip())


    with open(sys.argv[1]) as fd:
    for index,line in enumerate(fd):
    if index == flag_pos:
    print("nn".join(vals),"n")
    if line.strip() in vals:
    fd.readline()
    continue
    print(line.strip())


    This script can be saved as movelines.py and called as ./movelines.py input.txt



    AWK



    This is doable in gawk probably much more easily than sed:



    $ awk 'NR==FNR && $0=="ABAB" a[i++]=$0;getline;getline; a[i++]=$0; ; NR!=FNR if($0=="DDD") for(val in a) printf "%snn",a[val]; if($0 == "ABAB") getline;getline;getline; print $0 ' input.txt input.txt
    AAA

    BBB

    CCC

    ABAB

    ACAC

    DDD

    EEE

    FFF


    GGG

    HHH


    The trick here is that we pass the file to awk twice for reading, and distinguish between first reading that finds the lines we want to move and second reading where we actually move them.



    If your actual file does not have blank lines as in the example you provided, you just need one getline instead of two and "%sn" in the second part of the code will suffice.



    For readability, here's a multipline version of the code with comments:



    # on first reading NR != FNR, 
    # so lets store ABAB and the other line into array
    awk 'NR==FNR && $0=="ABAB"
    # i variable will be initialized once incremented first time
    a[i++]=$0;getline;getline; a[i++]=$0;
    ;
    # Here we are reading the same file second time
    NR!=FNR

    if($0=="DDD")
    for(val in a)
    printf "%snn",a[val];
    # Skip what we matched already
    if ($0 == "ABAB")
    getline;
    getline;
    getline;

    print $0
    ' input.txt input.txt





    share|improve this answer
































      0














      I have done by below method



      command



      Step1: h=`sed -n '/[A-Za-z]4/p' filename| sed -n '1p'`
      step2:m=`sed -n '/[A-Za-z]4/p' filename| sed -n '2p'`
      step3

      sed '/[A-Z]4/d' filename|sed "/CCC/s/.*/&nn$hnn$m/g"| sed '/^$/d'


      output



      AAA
      BBB
      CCC
      ABAB
      ACAC
      DDD
      EEE
      FFF
      GGG
      HHH





      share|improve this answer






















        Your Answer








        StackExchange.ready(function()
        var channelOptions =
        tags: "".split(" "),
        id: "106"
        ;
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function()
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled)
        StackExchange.using("snippets", function()
        createEditor();
        );

        else
        createEditor();

        );

        function createEditor()
        StackExchange.prepareEditor(
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: false,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: null,
        bindNavPrevention: true,
        postfix: "",
        imageUploader:
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        ,
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        );



        );













        draft saved

        draft discarded


















        StackExchange.ready(
        function ()
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f502622%2fmove-few-lines-after-a-pattern-match-to-another-position-just-before-another-ma%23new-answer', 'question_page');

        );

        Post as a guest















        Required, but never shown

























        4 Answers
        4






        active

        oldest

        votes








        4 Answers
        4






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        2














        NOTE: I'm assuming your data does not have blank lines between each entry; if it does, then you will need to address four lines i.e. change + to +3



        With GNU ed:



        $ ed -s file <<EOF
        /ABAB/,//+m?DDD?-
        ,p
        q
        EOF


        where




        • /ABAB/,//+ addresses a range of lines from /ABAB/ to the previous match // plus one line


        • m moves the addressed lines to


        • ?DDD?- the previous line matching DDD, minus one line


        • ,p print the whole buffer

        As a one-liner,



        printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s file


        To edit file in place, replace ,pnqn by ,wqn (write and quit).






        share|improve this answer























        • Works great! How do we handle this when we have to do this processing on output of a previously processed output? sat if I want to cat file and then do this processing after that, how do I do it?

          – Pratap
          Feb 24 at 7:10











        • @Pratap yes you can - but it's not just a matter of piping the previous command output, since ed reads its commands via standard input. Instead, you have to pass the command as a special "file", preceding it with a ! character e.g. printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s !'cat file'.

          – steeldriver
          Feb 24 at 14:20















        2














        NOTE: I'm assuming your data does not have blank lines between each entry; if it does, then you will need to address four lines i.e. change + to +3



        With GNU ed:



        $ ed -s file <<EOF
        /ABAB/,//+m?DDD?-
        ,p
        q
        EOF


        where




        • /ABAB/,//+ addresses a range of lines from /ABAB/ to the previous match // plus one line


        • m moves the addressed lines to


        • ?DDD?- the previous line matching DDD, minus one line


        • ,p print the whole buffer

        As a one-liner,



        printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s file


        To edit file in place, replace ,pnqn by ,wqn (write and quit).






        share|improve this answer























        • Works great! How do we handle this when we have to do this processing on output of a previously processed output? sat if I want to cat file and then do this processing after that, how do I do it?

          – Pratap
          Feb 24 at 7:10











        • @Pratap yes you can - but it's not just a matter of piping the previous command output, since ed reads its commands via standard input. Instead, you have to pass the command as a special "file", preceding it with a ! character e.g. printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s !'cat file'.

          – steeldriver
          Feb 24 at 14:20













        2












        2








        2







        NOTE: I'm assuming your data does not have blank lines between each entry; if it does, then you will need to address four lines i.e. change + to +3



        With GNU ed:



        $ ed -s file <<EOF
        /ABAB/,//+m?DDD?-
        ,p
        q
        EOF


        where




        • /ABAB/,//+ addresses a range of lines from /ABAB/ to the previous match // plus one line


        • m moves the addressed lines to


        • ?DDD?- the previous line matching DDD, minus one line


        • ,p print the whole buffer

        As a one-liner,



        printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s file


        To edit file in place, replace ,pnqn by ,wqn (write and quit).






        share|improve this answer













        NOTE: I'm assuming your data does not have blank lines between each entry; if it does, then you will need to address four lines i.e. change + to +3



        With GNU ed:



        $ ed -s file <<EOF
        /ABAB/,//+m?DDD?-
        ,p
        q
        EOF


        where




        • /ABAB/,//+ addresses a range of lines from /ABAB/ to the previous match // plus one line


        • m moves the addressed lines to


        • ?DDD?- the previous line matching DDD, minus one line


        • ,p print the whole buffer

        As a one-liner,



        printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s file


        To edit file in place, replace ,pnqn by ,wqn (write and quit).







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Feb 24 at 4:01









        steeldriversteeldriver

        37.2k45287




        37.2k45287












        • Works great! How do we handle this when we have to do this processing on output of a previously processed output? sat if I want to cat file and then do this processing after that, how do I do it?

          – Pratap
          Feb 24 at 7:10











        • @Pratap yes you can - but it's not just a matter of piping the previous command output, since ed reads its commands via standard input. Instead, you have to pass the command as a special "file", preceding it with a ! character e.g. printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s !'cat file'.

          – steeldriver
          Feb 24 at 14:20

















        • Works great! How do we handle this when we have to do this processing on output of a previously processed output? sat if I want to cat file and then do this processing after that, how do I do it?

          – Pratap
          Feb 24 at 7:10











        • @Pratap yes you can - but it's not just a matter of piping the previous command output, since ed reads its commands via standard input. Instead, you have to pass the command as a special "file", preceding it with a ! character e.g. printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s !'cat file'.

          – steeldriver
          Feb 24 at 14:20
















        Works great! How do we handle this when we have to do this processing on output of a previously processed output? sat if I want to cat file and then do this processing after that, how do I do it?

        – Pratap
        Feb 24 at 7:10





        Works great! How do we handle this when we have to do this processing on output of a previously processed output? sat if I want to cat file and then do this processing after that, how do I do it?

        – Pratap
        Feb 24 at 7:10













        @Pratap yes you can - but it's not just a matter of piping the previous command output, since ed reads its commands via standard input. Instead, you have to pass the command as a special "file", preceding it with a ! character e.g. printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s !'cat file'.

        – steeldriver
        Feb 24 at 14:20





        @Pratap yes you can - but it's not just a matter of piping the previous command output, since ed reads its commands via standard input. Instead, you have to pass the command as a special "file", preceding it with a ! character e.g. printf '/ABAB/,//+m?DDD?-n,pnqn' | ed -s !'cat file'.

        – steeldriver
        Feb 24 at 14:20













        1














        You wanted with sed so you can do it as shown:



        sed -e '
        /DDD/,/ABAB/! b
        H;/ABAB/!$!d;;g
        s/(n.*)n(.*)/21/
        ' input.txt


        This gets straightforward with the ed editor:



         ed -s input.file - <<eof
        /ABAB/m?DDD?-
        wq
        eof





        share|improve this answer





























          1














          You wanted with sed so you can do it as shown:



          sed -e '
          /DDD/,/ABAB/! b
          H;/ABAB/!$!d;;g
          s/(n.*)n(.*)/21/
          ' input.txt


          This gets straightforward with the ed editor:



           ed -s input.file - <<eof
          /ABAB/m?DDD?-
          wq
          eof





          share|improve this answer



























            1












            1








            1







            You wanted with sed so you can do it as shown:



            sed -e '
            /DDD/,/ABAB/! b
            H;/ABAB/!$!d;;g
            s/(n.*)n(.*)/21/
            ' input.txt


            This gets straightforward with the ed editor:



             ed -s input.file - <<eof
            /ABAB/m?DDD?-
            wq
            eof





            share|improve this answer















            You wanted with sed so you can do it as shown:



            sed -e '
            /DDD/,/ABAB/! b
            H;/ABAB/!$!d;;g
            s/(n.*)n(.*)/21/
            ' input.txt


            This gets straightforward with the ed editor:



             ed -s input.file - <<eof
            /ABAB/m?DDD?-
            wq
            eof






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Feb 24 at 4:14

























            answered Feb 24 at 3:59









            Rakesh SharmaRakesh Sharma

            342115




            342115





















                0














                Python (single read)



                Reading a file in one and storing what's between the two patterns can be done as so:



                #!/usr/bin/env python3
                import sys

                flag=False
                vals =
                with open(sys.argv[1]) as fd:
                for line in fd:
                if line.strip() == "DDD" or flag:
                # encountered line where we should stop appending values
                if line.strip() == "ABAB":
                flag = False
                # print the line and two others, then move in what was between originally
                print(line.strip())
                for i in range(2):
                print(fd.readline().strip())
                print("n".join(vals))
                continue
                # store values while we haven't hit ABAB
                flag = True
                vals.append(line.strip())
                continue

                print(line.strip())


                Python ( double read)



                Re-using original awk idea I had to read the file twice, we can do the same in Python:



                #!/usr/bin/env python3
                import sys


                flag_pos,match = 0,0
                vals =
                with open(sys.argv[1]) as fd:
                for index, line in enumerate(fd):
                if line.strip() == "DDD":
                flag_pos = index
                if line.strip() == "ABAB":
                vals.append(line.strip())
                fd.readline()
                vals.append(fd.readline().strip())


                with open(sys.argv[1]) as fd:
                for index,line in enumerate(fd):
                if index == flag_pos:
                print("nn".join(vals),"n")
                if line.strip() in vals:
                fd.readline()
                continue
                print(line.strip())


                This script can be saved as movelines.py and called as ./movelines.py input.txt



                AWK



                This is doable in gawk probably much more easily than sed:



                $ awk 'NR==FNR && $0=="ABAB" a[i++]=$0;getline;getline; a[i++]=$0; ; NR!=FNR if($0=="DDD") for(val in a) printf "%snn",a[val]; if($0 == "ABAB") getline;getline;getline; print $0 ' input.txt input.txt
                AAA

                BBB

                CCC

                ABAB

                ACAC

                DDD

                EEE

                FFF


                GGG

                HHH


                The trick here is that we pass the file to awk twice for reading, and distinguish between first reading that finds the lines we want to move and second reading where we actually move them.



                If your actual file does not have blank lines as in the example you provided, you just need one getline instead of two and "%sn" in the second part of the code will suffice.



                For readability, here's a multipline version of the code with comments:



                # on first reading NR != FNR, 
                # so lets store ABAB and the other line into array
                awk 'NR==FNR && $0=="ABAB"
                # i variable will be initialized once incremented first time
                a[i++]=$0;getline;getline; a[i++]=$0;
                ;
                # Here we are reading the same file second time
                NR!=FNR

                if($0=="DDD")
                for(val in a)
                printf "%snn",a[val];
                # Skip what we matched already
                if ($0 == "ABAB")
                getline;
                getline;
                getline;

                print $0
                ' input.txt input.txt





                share|improve this answer





























                  0














                  Python (single read)



                  Reading a file in one and storing what's between the two patterns can be done as so:



                  #!/usr/bin/env python3
                  import sys

                  flag=False
                  vals =
                  with open(sys.argv[1]) as fd:
                  for line in fd:
                  if line.strip() == "DDD" or flag:
                  # encountered line where we should stop appending values
                  if line.strip() == "ABAB":
                  flag = False
                  # print the line and two others, then move in what was between originally
                  print(line.strip())
                  for i in range(2):
                  print(fd.readline().strip())
                  print("n".join(vals))
                  continue
                  # store values while we haven't hit ABAB
                  flag = True
                  vals.append(line.strip())
                  continue

                  print(line.strip())


                  Python ( double read)



                  Re-using original awk idea I had to read the file twice, we can do the same in Python:



                  #!/usr/bin/env python3
                  import sys


                  flag_pos,match = 0,0
                  vals =
                  with open(sys.argv[1]) as fd:
                  for index, line in enumerate(fd):
                  if line.strip() == "DDD":
                  flag_pos = index
                  if line.strip() == "ABAB":
                  vals.append(line.strip())
                  fd.readline()
                  vals.append(fd.readline().strip())


                  with open(sys.argv[1]) as fd:
                  for index,line in enumerate(fd):
                  if index == flag_pos:
                  print("nn".join(vals),"n")
                  if line.strip() in vals:
                  fd.readline()
                  continue
                  print(line.strip())


                  This script can be saved as movelines.py and called as ./movelines.py input.txt



                  AWK



                  This is doable in gawk probably much more easily than sed:



                  $ awk 'NR==FNR && $0=="ABAB" a[i++]=$0;getline;getline; a[i++]=$0; ; NR!=FNR if($0=="DDD") for(val in a) printf "%snn",a[val]; if($0 == "ABAB") getline;getline;getline; print $0 ' input.txt input.txt
                  AAA

                  BBB

                  CCC

                  ABAB

                  ACAC

                  DDD

                  EEE

                  FFF


                  GGG

                  HHH


                  The trick here is that we pass the file to awk twice for reading, and distinguish between first reading that finds the lines we want to move and second reading where we actually move them.



                  If your actual file does not have blank lines as in the example you provided, you just need one getline instead of two and "%sn" in the second part of the code will suffice.



                  For readability, here's a multipline version of the code with comments:



                  # on first reading NR != FNR, 
                  # so lets store ABAB and the other line into array
                  awk 'NR==FNR && $0=="ABAB"
                  # i variable will be initialized once incremented first time
                  a[i++]=$0;getline;getline; a[i++]=$0;
                  ;
                  # Here we are reading the same file second time
                  NR!=FNR

                  if($0=="DDD")
                  for(val in a)
                  printf "%snn",a[val];
                  # Skip what we matched already
                  if ($0 == "ABAB")
                  getline;
                  getline;
                  getline;

                  print $0
                  ' input.txt input.txt





                  share|improve this answer



























                    0












                    0








                    0







                    Python (single read)



                    Reading a file in one and storing what's between the two patterns can be done as so:



                    #!/usr/bin/env python3
                    import sys

                    flag=False
                    vals =
                    with open(sys.argv[1]) as fd:
                    for line in fd:
                    if line.strip() == "DDD" or flag:
                    # encountered line where we should stop appending values
                    if line.strip() == "ABAB":
                    flag = False
                    # print the line and two others, then move in what was between originally
                    print(line.strip())
                    for i in range(2):
                    print(fd.readline().strip())
                    print("n".join(vals))
                    continue
                    # store values while we haven't hit ABAB
                    flag = True
                    vals.append(line.strip())
                    continue

                    print(line.strip())


                    Python ( double read)



                    Re-using original awk idea I had to read the file twice, we can do the same in Python:



                    #!/usr/bin/env python3
                    import sys


                    flag_pos,match = 0,0
                    vals =
                    with open(sys.argv[1]) as fd:
                    for index, line in enumerate(fd):
                    if line.strip() == "DDD":
                    flag_pos = index
                    if line.strip() == "ABAB":
                    vals.append(line.strip())
                    fd.readline()
                    vals.append(fd.readline().strip())


                    with open(sys.argv[1]) as fd:
                    for index,line in enumerate(fd):
                    if index == flag_pos:
                    print("nn".join(vals),"n")
                    if line.strip() in vals:
                    fd.readline()
                    continue
                    print(line.strip())


                    This script can be saved as movelines.py and called as ./movelines.py input.txt



                    AWK



                    This is doable in gawk probably much more easily than sed:



                    $ awk 'NR==FNR && $0=="ABAB" a[i++]=$0;getline;getline; a[i++]=$0; ; NR!=FNR if($0=="DDD") for(val in a) printf "%snn",a[val]; if($0 == "ABAB") getline;getline;getline; print $0 ' input.txt input.txt
                    AAA

                    BBB

                    CCC

                    ABAB

                    ACAC

                    DDD

                    EEE

                    FFF


                    GGG

                    HHH


                    The trick here is that we pass the file to awk twice for reading, and distinguish between first reading that finds the lines we want to move and second reading where we actually move them.



                    If your actual file does not have blank lines as in the example you provided, you just need one getline instead of two and "%sn" in the second part of the code will suffice.



                    For readability, here's a multipline version of the code with comments:



                    # on first reading NR != FNR, 
                    # so lets store ABAB and the other line into array
                    awk 'NR==FNR && $0=="ABAB"
                    # i variable will be initialized once incremented first time
                    a[i++]=$0;getline;getline; a[i++]=$0;
                    ;
                    # Here we are reading the same file second time
                    NR!=FNR

                    if($0=="DDD")
                    for(val in a)
                    printf "%snn",a[val];
                    # Skip what we matched already
                    if ($0 == "ABAB")
                    getline;
                    getline;
                    getline;

                    print $0
                    ' input.txt input.txt





                    share|improve this answer















                    Python (single read)



                    Reading a file in one and storing what's between the two patterns can be done as so:



                    #!/usr/bin/env python3
                    import sys

                    flag=False
                    vals =
                    with open(sys.argv[1]) as fd:
                    for line in fd:
                    if line.strip() == "DDD" or flag:
                    # encountered line where we should stop appending values
                    if line.strip() == "ABAB":
                    flag = False
                    # print the line and two others, then move in what was between originally
                    print(line.strip())
                    for i in range(2):
                    print(fd.readline().strip())
                    print("n".join(vals))
                    continue
                    # store values while we haven't hit ABAB
                    flag = True
                    vals.append(line.strip())
                    continue

                    print(line.strip())


                    Python ( double read)



                    Re-using original awk idea I had to read the file twice, we can do the same in Python:



                    #!/usr/bin/env python3
                    import sys


                    flag_pos,match = 0,0
                    vals =
                    with open(sys.argv[1]) as fd:
                    for index, line in enumerate(fd):
                    if line.strip() == "DDD":
                    flag_pos = index
                    if line.strip() == "ABAB":
                    vals.append(line.strip())
                    fd.readline()
                    vals.append(fd.readline().strip())


                    with open(sys.argv[1]) as fd:
                    for index,line in enumerate(fd):
                    if index == flag_pos:
                    print("nn".join(vals),"n")
                    if line.strip() in vals:
                    fd.readline()
                    continue
                    print(line.strip())


                    This script can be saved as movelines.py and called as ./movelines.py input.txt



                    AWK



                    This is doable in gawk probably much more easily than sed:



                    $ awk 'NR==FNR && $0=="ABAB" a[i++]=$0;getline;getline; a[i++]=$0; ; NR!=FNR if($0=="DDD") for(val in a) printf "%snn",a[val]; if($0 == "ABAB") getline;getline;getline; print $0 ' input.txt input.txt
                    AAA

                    BBB

                    CCC

                    ABAB

                    ACAC

                    DDD

                    EEE

                    FFF


                    GGG

                    HHH


                    The trick here is that we pass the file to awk twice for reading, and distinguish between first reading that finds the lines we want to move and second reading where we actually move them.



                    If your actual file does not have blank lines as in the example you provided, you just need one getline instead of two and "%sn" in the second part of the code will suffice.



                    For readability, here's a multipline version of the code with comments:



                    # on first reading NR != FNR, 
                    # so lets store ABAB and the other line into array
                    awk 'NR==FNR && $0=="ABAB"
                    # i variable will be initialized once incremented first time
                    a[i++]=$0;getline;getline; a[i++]=$0;
                    ;
                    # Here we are reading the same file second time
                    NR!=FNR

                    if($0=="DDD")
                    for(val in a)
                    printf "%snn",a[val];
                    # Skip what we matched already
                    if ($0 == "ABAB")
                    getline;
                    getline;
                    getline;

                    print $0
                    ' input.txt input.txt






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Feb 24 at 4:38

























                    answered Feb 24 at 3:39









                    Sergiy KolodyazhnyySergiy Kolodyazhnyy

                    10.6k42763




                    10.6k42763





















                        0














                        I have done by below method



                        command



                        Step1: h=`sed -n '/[A-Za-z]4/p' filename| sed -n '1p'`
                        step2:m=`sed -n '/[A-Za-z]4/p' filename| sed -n '2p'`
                        step3

                        sed '/[A-Z]4/d' filename|sed "/CCC/s/.*/&nn$hnn$m/g"| sed '/^$/d'


                        output



                        AAA
                        BBB
                        CCC
                        ABAB
                        ACAC
                        DDD
                        EEE
                        FFF
                        GGG
                        HHH





                        share|improve this answer



























                          0














                          I have done by below method



                          command



                          Step1: h=`sed -n '/[A-Za-z]4/p' filename| sed -n '1p'`
                          step2:m=`sed -n '/[A-Za-z]4/p' filename| sed -n '2p'`
                          step3

                          sed '/[A-Z]4/d' filename|sed "/CCC/s/.*/&nn$hnn$m/g"| sed '/^$/d'


                          output



                          AAA
                          BBB
                          CCC
                          ABAB
                          ACAC
                          DDD
                          EEE
                          FFF
                          GGG
                          HHH





                          share|improve this answer

























                            0












                            0








                            0







                            I have done by below method



                            command



                            Step1: h=`sed -n '/[A-Za-z]4/p' filename| sed -n '1p'`
                            step2:m=`sed -n '/[A-Za-z]4/p' filename| sed -n '2p'`
                            step3

                            sed '/[A-Z]4/d' filename|sed "/CCC/s/.*/&nn$hnn$m/g"| sed '/^$/d'


                            output



                            AAA
                            BBB
                            CCC
                            ABAB
                            ACAC
                            DDD
                            EEE
                            FFF
                            GGG
                            HHH





                            share|improve this answer













                            I have done by below method



                            command



                            Step1: h=`sed -n '/[A-Za-z]4/p' filename| sed -n '1p'`
                            step2:m=`sed -n '/[A-Za-z]4/p' filename| sed -n '2p'`
                            step3

                            sed '/[A-Z]4/d' filename|sed "/CCC/s/.*/&nn$hnn$m/g"| sed '/^$/d'


                            output



                            AAA
                            BBB
                            CCC
                            ABAB
                            ACAC
                            DDD
                            EEE
                            FFF
                            GGG
                            HHH






                            share|improve this answer












                            share|improve this answer



                            share|improve this answer










                            answered Feb 24 at 16:18









                            Praveen Kumar BSPraveen Kumar BS

                            1,6351311




                            1,6351311



























                                draft saved

                                draft discarded
















































                                Thanks for contributing an answer to Unix & Linux Stack Exchange!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid


                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.

                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function ()
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f502622%2fmove-few-lines-after-a-pattern-match-to-another-position-just-before-another-ma%23new-answer', 'question_page');

                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown






                                Popular posts from this blog

                                Peggy Mitchell

                                Palaiologos

                                The Forum (Inglewood, California)