cyfi
cyfi

Reputation: 11

Moving a specific line under another using sed

I need to find and replace entryUUID with newrdn and then move that line under another line. This text file will have over 500k changetypes so moving to a specific line number will not work.

Original lines in text file:

dn: [email protected],ou=people,dc=example,dc=com
changetype: modrdn
deleteoldrdn: 1
newsuperior: ou=people,dc=example,dc=com
entryUUID: 6277b4d4e3-xxxx-xxxx-xxxx-xxxxxxxxxx

I want to replace entryUUID with newrdn and then move this line below the line starting with changetype.

ideal output:

dn: [email protected],ou=people,dc=example,dc=com
changetype: modrdn
newrdn: 6277b4d4e3-xxxx-xxxx-xxxx-xxxxxxxxxx
deleteoldrdn: 1
newsuperior: ou=people,dc=example,dc=com

I'm using the following command; but, it is not working as expected (adding a new incorrect entry instead of moving the line)

sed -i '/^changetype:*/a;'s/entryUUID:/newrdn:/g' file.txt

Upvotes: 1

Views: 265

Answers (3)

potong
potong

Reputation: 58391

This might work for you (GNU sed):

sed -n '/^changetype:/{p;:a;n;/^entryUUID:/bb;H;ba;:b;s//newrdn:/p;z;x;s/.//};p' file

Print any lines until one beginning changetype:, print this line and gather up the lines following it into the hold space until a line beginning entryUUID:. Replace that string by newrdn: and print the current line. Append the lines collected in the hold space less the leading newline and print them. Print all other lines.

An alternative solution:

sed '/^changetype:/{:a;N;s/^entryUUID:/newrnd:/m;Ta;s/\(\n.*\)\(\n.*\)/\2\1/}' file

Upvotes: 0

Shawn
Shawn

Reputation: 52344

Instead of using sed -i to edit the file in-place, you can also use ed, which, since it treats a file as a whole instead of processing an isolated line at a time, has commands to move lines around, which simplifies things a lot. Using the same input file as my sed answer:

$ ed -s input.txt <<'EOF'
g/^entryUUID:/s/^entryUUID:/newrdn:/\ 
m -3
w
EOF
$ cat input.txt
dn: [email protected],ou=people,dc=example,dc=com
changetype: modrdn
newrdn: 6277b4d4e3-xxxx-xxxx-xxxx-xxxxxxxxxx
deleteoldrdn: 1
newsuperior: ou=people,dc=example,dc=com
---
dn: [email protected],ou=people,dc=example,dc=com
changetype: modrdn
newrdn: 7277b4d4e3-xxxx-xxxx-xxxx-xxxxxxxxxx
deleteoldrdn: 1
newsuperior: ou=people,dc=example,dc=org

How it works

For each line starting with entryUUID: (g/^entryUUID:/), first change that line so it starts with newrdn:, and then move it up two lines (m moves to after the destination address, hence -3 instead of -2.) Then write the modified file after making all the changes.

Putting the commands in a heredoc instead of just entering them interactively makes it easier to use in a script. Using single quotes around the EOF is needed to keep the backslash from being eaten.

Upvotes: 0

Shawn
Shawn

Reputation: 52344

One way using sed:

Example file with multiple entries:

dn: [email protected],ou=people,dc=example,dc=com
changetype: modrdn
deleteoldrdn: 1
newsuperior: ou=people,dc=example,dc=com
entryUUID: 6277b4d4e3-xxxx-xxxx-xxxx-xxxxxxxxxx
---
dn: [email protected],ou=people,dc=example,dc=com
changetype: modrdn
deleteoldrdn: 1
newsuperior: ou=people,dc=example,dc=org
entryUUID: 7277b4d4e3-xxxx-xxxx-xxxx-xxxxxxxxxx
sed -e '/^deleteoldrdn:/{N;h;d}; /^entryUUID:/{s/^entryUUID:/newrdn:/;G}' input.txt
dn: [email protected],ou=people,dc=example,dc=com
changetype: modrdn
newrdn: 6277b4d4e3-xxxx-xxxx-xxxx-xxxxxxxxxx
deleteoldrdn: 1
newsuperior: ou=people,dc=example,dc=com
---
dn: [email protected],ou=people,dc=example,dc=com
changetype: modrdn
newrdn: 7277b4d4e3-xxxx-xxxx-xxxx-xxxxxxxxxx
deleteoldrdn: 1
newsuperior: ou=people,dc=example,dc=org

How it works:

/^deleteoldrnd:/{N;h;d}

Each time a line that starts with deleteoldrnd: is read, also read the next line and add it to the current pattern space (N), replace the current hold space with the pattern space (h), delete the pattern space and begin anew with a new line (d).

/^entryUUID:/{s/^entryUUID:/newrdn:/;G}

Each time a line that starts with entryUUID: is read, change the prefix to newrdn:, then append the hold space to the pattern space (G), (The hold space at this point is the deleteoldrdn and newsuperior lines immediately preceding in the original file). The pattern space then automatically gets printed out.

If you're using GNU sed (Which seems likely since you tagged this [linux]), a variation is:

sed -e '/^deleteoldrdn:/{N;h;z;N;s/\nentryUUID:/newrdn:/;G}' input.txt 

which uses the GNU extension z to erase the pattern space before reading the UUID line in the same command sequence, instead of splitting it up into two.

Upvotes: 1

Related Questions