查找并替换并移动包含特定字符串的行



假设我有以下文本文件:

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)的行可以出现在文件中的任何位置,这些行不按任何顺序

我的方法是

  1. 删除我要替换的行
  2. 查找之前的行我想把它移到
  3. 之后
  4. 查找之后的行我想把它移到
  5. 之后
  6. 将输出附加到文件
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

但是这个解决方案有很多问题:

  1. 许多看起来不必要的grep命令
  2. 参数-A999-B999假设文件不包含超过999行,最好有另一种方法来获取匹配行
  3. 之前和之后的行

我正在寻找一种更有效的方法来实现这一目标

使用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

不管ek在文件中的顺序:

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

最新更新