Python (single read)
Reading a file in one and storing what's between the two patterns can be done as so:
#!/usr/bin/env python3
import sys
flag=False
vals = []
with open(sys.argv[1]) as fd:
for line in fd:
if line.strip() == "DDD" or flag:
# encountered line where we should stop appending values
if line.strip() == "ABAB":
flag = False
# print the line and two others, then move in what was between originally
print(line.strip())
for i in range(2):
print(fd.readline().strip())
print("\n".join(vals))
continue
# store values while we haven't hit ABAB
flag = True
vals.append(line.strip())
continue
print(line.strip())
Python ( double read)
Re-using original awk idea I had to read the file twice, we can do the same in Python:
#!/usr/bin/env python3
import sys
flag_pos,match = 0,0
vals = []
with open(sys.argv[1]) as fd:
for index, line in enumerate(fd):
if line.strip() == "DDD":
flag_pos = index
if line.strip() == "ABAB":
vals.append(line.strip())
fd.readline()
vals.append(fd.readline().strip())
with open(sys.argv[1]) as fd:
for index,line in enumerate(fd):
if index == flag_pos:
print("\n\n".join(vals),"\n")
if line.strip() in vals:
fd.readline()
continue
print(line.strip())
This script can be saved as movelines.py
and called as ./movelines.py input.txt
AWK
This is doable in gawk
probably much more easily than sed
:
$ awk 'NR==FNR && $0=="ABAB" { a[i++]=$0;getline;getline; a[i++]=$0; }; NR!=FNR { if($0=="DDD") for(val in a) printf "%s\n\n",a[val]; if($0 == "ABAB") {getline;getline;getline;} print $0 }' input.txt input.txt
AAA
BBB
CCC
ABAB
ACAC
DDD
EEE
FFF
GGG
HHH
The trick here is that we pass the file to awk
twice for reading, and distinguish between first reading that finds the lines we want to move and second reading where we actually move them.
If your actual file does not have blank lines as in the example you provided, you just need one getline
instead of two and "%s\n"
in the second part of the code will suffice.
For readability, here's a multipline version of the code with comments:
# on first reading NR != FNR,
# so lets store ABAB and the other line into array
awk 'NR==FNR && $0=="ABAB" {
# i variable will be initialized once incremented first time
a[i++]=$0;getline;getline; a[i++]=$0;
};
# Here we are reading the same file second time
NR!=FNR {
if($0=="DDD")
for(val in a)
printf "%s\n\n",a[val];
# Skip what we matched already
if ($0 == "ABAB"){
getline;
getline;
getline;
}
print $0
}' input.txt input.txt