Saurav Kumar Ghosh
Saurav Kumar Ghosh

Reputation: 123

change the position of a line in a file using sed

I would like to know how to change the position of a line in a file (preferably using sed). For example, consider the file that contains

goal identifier statement  
let statement 1  
let statement 2  
forall statement  
other statements

I would like to be able to do this

goal identifier statement  
forall statement  
let statement 1  
let statement 2  
other statements  

where I change the position of the forall line and bring it after the goal line. forall and goal are regexps that can be used to identify the lines.

Upvotes: 3

Views: 2830

Answers (5)

jeremysprofile
jeremysprofile

Reputation: 11504

A less terrible way using vim to find a line in $FILENAME using regex $REGEX_EXPRESSION and move that line to $LINE_NUMBER:

vim -c "g:$REGEX_EXPRESSION:m$LINE_NUMBER" -cwq "$FILENAME"

explanation: -c is command in vim, so it goes to the first line that matches that regex and then moves it to the line number specified, and then does the command wq (or write and quit).

Upvotes: 1

mug896
mug896

Reputation: 2025

sed -r '/goal/{                 # if match "goal" line
    :X                          # this is a lable for branch command
    N                           # append next line
    /forall[^\n]*$/{            # if match "forall" line move to "goal" line below
        s#^([^\n]*)(.*)(\n[^\n]*)$#\1\3\2# 
        b                       # after move finished branch to end
    } 
    bX                          # branch to :X for appending next line
}' file
goal identifier statement  
forall statement  
let statement 1  
let statement 2  
other statements

Upvotes: 0

Ed Morton
Ed Morton

Reputation: 204578

sed is for simple substitutions on individual lines, that is all. For anything else you should use awk for every desirable attribute of software (clarity, simplicity, portability, etc.:

$ awk 'NR==FNR{if (/forall/) {f=FNR; v=$0} next} FNR!=f; /goal/{print v} ' file file
goal identifier statement
forall statement
let statement 1
let statement 2
other statements

Upvotes: 0

Jose Ricardo Bustos M.
Jose Ricardo Bustos M.

Reputation: 8174

you can try, for move line 4 to line 2, I want to move line A to line B, where A>B

sed -n '2{h; :a; n; 4{p;x;bb}; H; ba}; :b; p' file

or A<B

sed -n '2{h; d}; 4{p; x;}; p' file

you get, in first case: move line 4 to line 2

goal identifier statement
forall statement
let statement 1
let statement 2
other statements

you get, in second case: move line 2 to line 4

goal identifier statement  
let statement 2  
forall statement  
let statement 1  
other statements

Explanation

sed -n '              #silent option ON
    2{                #if is line 2
        h             #Replace the contents of the hold space with the contents of the pattern space
        :a            #label "a"
        n             #fetch the next line
        4{            #if is line 4
            p         #print line 4
            x         #Exchange the contents of the hold and pattern spaces
            bb        #goto "b"
        }
        H             #appends line from the pattern space to the hold space, with a newline before it.
        ba            #goto "a"
    }
    :b                #Label "b"
    p                 #print
' file 

EDIT

If You want use regex for identify the lines, you can modify first command

sed -n '/goal/{p;n;h;:a;n;/forall/{p;x;bb};H;ba};:b;p' file

Upvotes: 6

slitvinov
slitvinov

Reputation: 5768

$ cat r.awk
BEGIN {
    forall_re = "^forall" # examples of regexps
    goal_re   = "^goal"
}

function tag(l) { # tag a line
    if      (l ~ goal_re  ) return "goal"
    else if (l ~ forall_re) return "forall"
    else                    return "rest"
}

{ # store entire file in array; give a tag to every line
    lines[NR] = $0 
    tags[NR]  = tag($0)
}

function swap0(a, i, j,   tmp) {
    tmp = a[i]; a[i] = a[j]; a[j] = tmp
}

function swap(i, j) {
    swap0(lines, i, j); swap0(tags, i, j)
}

function rise(i) {
    # TODO: add error check
    while (i - 1 > 0 && tags[i - 1] != "goal") {
        swap(i, i - 1); i--
    }
}

function process(    i) {
    for (i = 1; i <= NR; i++)
        if (tags[i] == "forall") rise(i)
}

function dump(    i) { # print the array
    for (i = 1; i <= NR; i++)
        print lines[i]
}

END {
    process()
    dump()
}

An example of input file

$ cat r.txt
goal identifier statement  
let statement 1  
let statement 2  
forall statement A  
other statements

goal identifier statement  
let statement 1  
let statement 2  
forall statement B
other statements

Usage:

$ awk -f r.awk r.txt
goal identifier statement  
forall statement A  
let statement 1  
let statement 2  
other statements

goal identifier statement  
forall statement B
let statement 1  
let statement 2  
other statements

Upvotes: 1

Related Questions