Reputation: 11331
I want to swap two lines, but only if they're in the wrong order. For example, if I have XML lines (anywhere in the file, not necessary in certain locations) like this:
<person>
<given-name>John</given-name>
<surname>Smith</surname>
</person>
I want to swap lines two and three, so that it produces
<person>
<surname>Smith</surname>
<given-name>John</given-name>
</person>
But only for files where those lines are out of order. Is there a way to do that with sed, or other linux tool?
Upvotes: 3
Views: 586
Reputation: 41460
If you like surname
to come before given-name
and have this data:
cat file
<person>
<given-name>John</given-name>
<surname>Smith</surname>
</person>
<person>
<surname>Hanson</surname>
<given-name>Thor</given-name>
</person>
This awk
will then change the order of surname
and given-name
if its wrong:
awk '/<person>/ {f=NR} f && f+1==NR && /<given-name>/ {a=$0;getline;print $0 RS a;next} 1' file
<person>
<surname>Smith</surname>
<given-name>John</given-name>
</person>
<person>
<surname>Hanson</surname>
<given-name>Thor</given-name>
</person>
How it works:
awk '
/<person>/ { # if line include <person> do:
f=NR} # do set flag "f" to line record
f && f+1==NR && /<given-name>/ { # if flag "f" is true and flag "f+1" equal "NR" and line include <given-name> do:
a=$0 # set "a" to current line
getline # get next line
print $0 RS a # print current line and previous line
next} # skip to next record
1 # print all other lines
' file # read the file
Upvotes: 0
Reputation: 58578
This might work for you (GNU sed);
sed -r '$!N;s/^(\s*<given-name>.*)\n(\s*<surname>.*)/\2\n\1/;P;D' file
Read 2 lines at a time and if the combination is wrong swap the lines.
Upvotes: 2
Reputation: 247220
Assuming that "line 4" and "line 3" are patterns and not the entire lines:
awk -v first="line 4" -v second="line 3" '
$0 ~ second { seen_second = 1 }
$0 ~ first && ! seen_second {
this_line = $0
# assume the second line is the *next* line
getline
print
print this_line
next
}
1
' file
This does not modify the file. To do that:
awk '...' file > tempfile &&
mv file file.bak &&
mv tempfile file
Upvotes: 1