假设我有以下文本文件:
a b c d 1 2 3
e f g h 1 2 3
i j k l 1 2 3
m n o p 1 2 3
如何将包含字母(e)和的行中的'1 2 3'替换为'4 5 6'把它移到包含字母(k)的行后面?
注意:包含字母(k)的行可以出现在文件中的任何位置,这些行不按任何顺序
我的方法是
- 删除我要替换的行
- 查找之前的行我想把它移到 之后
- 查找之后的行我想把它移到 之后
- 将输出附加到文件
grep -v 'e' $original > $file
grep -B999 'k' $file > $output
grep 'e' $original | sed 's/1 2 3/4 5 6/' >> $output
grep -A999 'k' $file | tail -n+2 >> $output
rm $file
mv $output $original
但是这个解决方案有很多问题:
- 许多看起来不必要的
grep
命令 - 参数
-A999
和-B999
假设文件不包含超过999行,最好有另一种方法来获取匹配行 之前和之后的行
我正在寻找一种更有效的方法来实现这一目标
使用sed
$ sed '/e/{s/1 2 3/4 5 6/;h;d};/k/{G}' input_file
a b c d 1 2 3
i j k l 1 2 3
e f g h 4 5 6
m n o p 1 2 3
下面是一个GNU awk解决方案:
awk '
/<e>/{
s=$0
sub("1 2 3", "4 5 6", s)
next
}
/<k>/ && s {
printf("%sn%sn",$0,s)
next
} 1
' file
或POSIX awk:
awk '
function has(x) {
for(i=1; i<=NF; i++) if ($i==x) return 1
return 0
}
has("e") {
s=$0
sub("1 2 3", "4 5 6", s)
next
}
has("k") && s {
printf("%sn%sn",$0,s)
next
} 1
' file
打印:
a b c d 1 2 3
i j k l 1 2 3
e f g h 4 5 6
m n o p 1 2 3
不管e
和k
在文件中的顺序:
awk '
function has(x) {
for(i=1; i<=NF; i++) if ($i==x) return 1
return 0
}
has("e") {
s=$0
sub("1 2 3", "4 5 6", s)
next
}
FNR<NR && has("k") && s {
printf("%sn%sn",$0,s)
s=""
next
}
FNR<NR
' file file
这个awk
应该适合你:
awk '
/(^| )e( |$)/ {
sub(/1 2 3/, "4 5 6")
p = $0
next
}
1
/(^| )k( |$)/ {
print p
p = ""
}' file
a b c d 1 2 3
i j k l 1 2 3
e f g h 4 5 6
m n o p 1 2 3
这可能适合您(GNU sed):
sed -n '/e/{s/1 2 3/4 5 6/;s#.*#/e/d;/k/s/.*/&\n&/#p};' file | sed -f - file
通过两次传递文件并将sed指令从第一次传递应用到第二次传递来设计sed脚本。
另一个解决方案是使用ed:
cat <<! | ed file
/e/s/1 2 3/4 5 6/
/e/m/k/
wq
!
或者:
<<<$'/e/s/1 2 3/4 5 6/n.m/k/nwq' ed -s file