找到指定字符串时修改行

找到指定字符串时修改行

我有一个看起来像这样的文件

ABC
     2   3   4
     7   9   4
     1   2   5
ABC
     13  11  17
     2   1   1
ABC
     7   9   14
     5   8   2
     9   9   9
     7   1   2

我想在每个“ABC”值的末尾打印单词“END”,所以文件将是这样的

ABC
     2   3   4
     7   9   4
END  1   2   5
ABC
     13  11  17
END  2   1   1
ABC
     7   9   14
     5   8   2
     9   9   9
END  7   1   2

我尝试了很多,但没有解决,所以有人可以帮忙吗?

答案1

awk '/^ABC/ && pre { print dpre ORS $0; pre=""; next }
                   { if(pre) print pre; pre=dpre=$0; sub(/ {0,4}/, "END ", dpre) }
END{ if(pre) print dpre }' infile

仅当行以以下开头时才会执行第一个块ABC 字符串和临时变量pre也被设置,否则下一个块将被执行。

END{...}块只会在全部结束后执行一次。

第一行当然还是pre变量尚未设置,因此将执行第二个块,它执行以下操作:

  • 如果里面有内容,pre请先打印它if(pre) print pre(这样我们会延迟前一行的打印,以检查下一行是否以 开头ABC,因为我们需要END在该行前面添加)

  • 然后我们将该行复制到两个单独的变量中predpre其中一个变量将保持不变(稍后我们需要将其打印不变),而对于另一个变量,sub(/ {0,3}/,"END ", dpre)我们将END字符串添加到dpre.

    请注意,使用{0,4}(零个或最多 4 个空格;4 是从 的长度获得的END<SPC>),我们确保END字符串始终被添加到前面,并防止在根本没有空格的情况下截断原始行值。


您可以在下面跟踪该命令的每次迭代以供您自己理解:

  • 重复
  • 读一行;是从ABC( /^ABC/)?
    • ;然后什么也不做,下一个块将被执行;去第二
    • 是的;被pre设定了吗?
      • 是的,然后做这些
        • 打印变量的内容dpre,然后打印单个换行符ORS,然后打印当前行本身
        • 空变量pre=""并跳转到重复因为next声明告诉了这一点。
      • ;然后什么也不做,下一个块将被执行;去第二
  • 第二
    • pre设置了吗?
      • 是的;做这些
        • 打印pre它的设置在“ if(pre) print pre”中;
        • 将当前行更新为两个变量pre=dpre=$0
        • 前置END字符串dpre.
      • ;做这些
        • 将当前行更新为两个变量pre=dpre=$0
        • 前置END字符串dpre.
  • 如果文件结束;打印最后的状态dpre如果设置了变量,则重复
  • 结束

答案2

$ cat tst.awk
NF == 1 { sub(/^   /,"END",prev) }
NR > 1 { print prev }
{ prev = $0 }
END { sub(/^   /,"END",prev); print prev }

$ awk -f tst.awk file
ABC
     2   3   4
     7   9   4
END  1   2   5
ABC
     13  11  17
END  2   1   1
ABC
     7   9   14
     5   8   2
     9   9   9
END  7   1   2

答案3

POSIX sed模式空间自动打印关闭 ( -n)

sed -ne '
  /ABC/!{x;1!p;$!d;}
  x;1!s/^ \{0,3\}/END&/p
  ${x;/ABC/p;}
'  file
ABC
     2   3   4
     7   9   4
END  1   2   5
ABC
     13  11  17
END  2   1   1
ABC
     7   9   14
     5   8   2
     9   9   9
END  7   1   2   

将上面的sed代码展开并注释掉。

sed -ne '
  # if we see a non boundary line...
  /ABC/!{
    x;           # store it n retrieve prev line
    1!p;         # print the prev line if not first (obviously)
    $!d;         # printed prev so go back n read next line unless we are @eof(for which see below...)
  }
  x;                  # retrieve prev
  1!s/^ \{0,3\}/END&/p; # add marker
  ${x;/ABC/p;}
  # To account for a trailing ABC
'  file

答案4

珀尔可以在这种情况下使用,并且由于其强大的正则表达式功能可以将其整理为真正的一行。

perl-0777在自动打印当前记录打开的slurp 模式下-p,我们检查 ABC 行或 eof 后面的所有行。在这两种情况下,都会在开头插入字符串“END”,最多覆盖 3 个前导空白字符。

perl -0777pe '
  s/^(?!ABC)\h{0,3}(?=.*\n(?:ABC|\z))/END/mg;
' file

正则表达式的研究:

  • 查找不以 ABC 开头的行^(?!ABC)
  • 开头没有到 3 个空格(至少)\h{0,3}
  • 从这个有利位置,我们是否能够看到当前行的末尾(?=.*\n(?:ABC|\z),并从那里看到以 ABC 开头的下一行,或者它是 eof。
  • 执行替换。了解环视不消耗数据,而是断言匹配位置。

相关内容