我有一个看起来像这样的文件
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
在该行前面添加)然后我们将该行复制到两个单独的变量中
pre
(dpre
其中一个变量将保持不变(稍后我们需要将其打印不变),而对于另一个变量,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。 - 执行替换。了解环视不消耗数据,而是断言匹配位置。