1

This question already has an answer here:

Hi assume I have a files that look like:

x
x
x
A
B
C
1
2
3
D
E
F
x
x
x

and what need the files to look like:

x
x
x
A
B
C
D
E
F
1
2
3
x
x
x

I know I could do this with a few sed -i s/search/replace/g lines but am wondering if there's a better, easier way to move x number of rows by y numbers in either direction?

marked as duplicate by don_crissti, Shadur, user88036, Romeo Ninov, jimmij Oct 16 '18 at 13:24

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

3

ed

echo -e '7,9m12\n,p' | ed -s file.txt
  • 1
    this solution works only under bash, it doesn't work under other environments – user90704 Oct 15 '18 at 22:01
3

Using sed (moving rows down, because that's easiest with stream editors):

$ sed '7,9{H;d;}; 12{G;s/\n//;}' <file
x
x
x
A
B
C
D
E
F
1
2
3
x
x
x

You may read the sed script as "move lines 7 through to 9 to after line 12".

The script annotated:

7,9{            # These are the lines we'd like to move
    H;          # Append them to the hold space (separated by newlines)
    d;          # Delete from pattern space, start next cycle
}
12{             # This is where we'd like to move those lines
    G;          # Append the hold space
    s/\n//;     # Remove the newline that the above command inserted
}
                # (implicit print)

Since lines may be addressed by regular expressions (not just line numbers), the following would work too (on this particular data):

sed '/^1/,/^3/{H;d;}; /^F/{G;s/\n//;}' <file

I.e., "move the lines from the one starting with 1 through to the one starting with 3 to after the line that starts with F".


A direct translation of the above into awk:

awk '/^1/,/^3/ { hold = hold ORS $0; next }
     /^F/      { $0 = $0 hold }
               { print }' <file

Using "line numbers" (record numbers in awk):

awk 'NR == 7, NR == 9 { hold = hold ORS $0; next }
     NR == 12         { $0 = $0 hold }
                      { print }' <file

The condition NR == 7, NR == 9 could also be written NR >= 7 && NR <= 9.

To be 100% faithful to the sed code, the second block should read { $0 = $0 ORS hold; sub(ORS, "", $0) } but it's a bit silly to insert the output record separator (a newline by default) only to remove it immediately afterwards.


To replace the original file with the result of the above commands:

sed ...as above... <file >newfile && mv newfile file

or the same thing with the awk command.

This would leave the original file unaltered if the input file of sed/awk could not be read, or if the output file could not be written.

  • 1
    robust solution thumb up! – user90704 Oct 15 '18 at 22:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.