在与模式匹配的每行之前插入换行符,除非前一行已经为空

在与模式匹配的每行之前插入换行符,除非前一行已经为空

我需要在包含模式的任何行之前添加一个新行,我们可以假设该模式始终是当前行的第一个字符串。例如

This is a
pattern
This is a
pattern

sed我可以使用命令添加新行

sed -i 's/pattern\+/\n&/g' file

得到输出

This is a

pattern
This is a

pattern

为了防止添加多个新行(在多次执行的情况下),我想检查模式之前的行是否为空。我知道我可以做到这一点

if [ "$line" == "" ]; then

但是,我首先如何确定匹配模式的前一行呢?

编辑:模式可以出现多次。

答案1

您可以将前一行存储在保留空间中:

sed '
 /^pattern/{
   x
   /./ {
     x
     s/^/\
/
     x
   }
   x
 }
 h'

不过,这样会更清晰awk

awk '!previous_empty && /pattern/ {print ""}
     {previous_empty = $0 == ""; print}'

就像 GNU 实现sed有一个-i就地编辑选项一样,GNU 实现也awk-i /usr/share/awk/inplace.awk¹ 功能。


^不使用-i inplaceas尝试首先从当前工作目录gawk加载inplace扩展(asinplace或),有人可能已经在其中植入了恶意软件。随系统提供的扩展inplace.awk的路径可能会有所不同,请参阅输出inplacegawkgawk 'BEGIN{print ENVIRON["AWKPATH"]}'

答案2

我知道你的问题最初是关于 sed 的,但 vim 中有一个非常简单的答案:

:g/.\npattern/norm o

或者,如果您希望完全从命令行运行:

vim file -c "g/.\npattern/norm o" -c "wq"

它的工作方式是查找与以下正则表达式匹配的任何行:

.\npattern

这是您的模式后面的任何非空行。然后,对于每个匹配,它应用以下命令norm o,该命令在当前光标位置下方打开一个换行符。

答案3

但是,我首先如何确定匹配模式的前一行呢?

嗯...也许这会起作用。

使用grep-B开关:

 -B num, --before-context=num
         Print num lines of leading context before each match.  See
         also the -A and -C options.

考虑这个 infile:

cat infile 
foo

bar
baz

现在,如果我grepfor bar,前一行应该为空:

if [[ $(grep -B 1 'bar' infile | head -1) == "" ]]; then echo "line is empty"; fi
line is empty

与使用grepon相反baz,前一行是不是空的:

if [[ $(grep -B 1 'baz' infile | head -1) == "" ]]; then echo "line is empty"; fi
<no output>

答案4

如果我们有 gnused (Linux 和许多其他系统中的默认设置,并且可供所有人使用)

sed -zri  's/([^\n]\n)(pattern)/\1\n\2/g' file

在哪里

  • ([^\n]\n)(pattern)非空行之后的模式
  • -z用空字符分隔“行”(slurp 文件)
  • -r具有扩展的正则表达式

相关内容