Jonathan
Jonathan

Reputation: 11331

How can I conditionally swap two lines with sed or other linux tool?

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

Answers (3)

Jotne
Jotne

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 surnameand 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

potong
potong

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

glenn jackman
glenn jackman

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

Related Questions