举个例子来说明问题。我有一个文件
AAA
BBB
CCC
DDD
EEE
FFF
ABAB
ACAC
GGG
HHH
我想在比赛结束后移动 2 行阿巴布到之前DDD。因此,修改后的文件将如下所示:
AAA
BBB
CCC
ABAB
ACAC
DDD
EEE
FFF
GGG
HHH
寻找一些很酷的方法来处理这个问题,最好使用 sed 。
答案1
注意:我假设你的数据确实如此不是每个条目之间有空行;如果是这样,那么您将需要解决四行问题,即更改+
为+3
使用 GNU ed
:
$ ed -s file <<EOF
/ABAB/,//+m?DDD?-
,p
q
EOF
在哪里
/ABAB/,//+
/ABAB/
寻址从上一个匹配到的一系列行//
加上一行m
将指定的行移动到?DDD?-
前一行匹配DDD
,减去一行,p
打印整个缓冲区
作为单线,
printf '/ABAB/,//+m?DDD?-\n,p\nq\n' | ed -s file
要file
就地编辑,请替换,p\nq\n
为,wq\n
(w仪式和q伊特)。
答案2
你想要的sed
,所以你可以这样做,如下所示:
sed -e '
/DDD/,/ABAB/! b
H;/ABAB/!{$!d;};g
s/\(\n.*\)\n\(.*\)/\2\1/
' input.txt
这对编辑来说很简单ed
:
ed -s input.file - <<\eof
/ABAB/m?DDD?-
wq
eof
答案3
Python(单读)
读取一个文件并存储两个模式之间的内容可以如下完成:
#!/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(双读)
重新使用原始的 awk 想法,我必须读取文件两次,我们可以在 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())
该脚本可以另存为movelines.py
并调用为./movelines.py input.txt
AWK
这gawk
可能比以下更容易实现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
这里的技巧是我们将文件传递awk
两次以进行读取,并区分第一次读取找到我们想要移动的行和第二次读取我们实际移动它们的位置。
如果您的实际文件没有如您提供的示例中所示的空行,则您只需要一个getline
而不是两个空行,并且"%s\n"
在代码的第二部分中就足够了。
为了便于阅读,这里是带有注释的代码的多行版本:
# 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
答案4
我已经通过以下方法完成了
命令
Step1: h=`sed -n '/[A-Za-z]\{4\}/p' filename| sed -n '1p'`
step2:m=`sed -n '/[A-Za-z]\{4\}/p' filename| sed -n '2p'`
step3
sed '/[A-Z]\{4\}/d' filename|sed "/CCC/s/.*/&\n\n$h\n\n$m/g"| sed '/^$/d'
输出
AAA
BBB
CCC
ABAB
ACAC
DDD
EEE
FFF
GGG
HHH