![如果数据开头有 % 百分比符号,如何在两个字之间移动数据?](https://linux22.com/image/30348/%E5%A6%82%E6%9E%9C%E6%95%B0%E6%8D%AE%E5%BC%80%E5%A4%B4%E6%9C%89%20%25%20%E7%99%BE%E5%88%86%E6%AF%94%E7%AC%A6%E5%8F%B7%EF%BC%8C%E5%A6%82%E4%BD%95%E5%9C%A8%E4%B8%A4%E4%B8%AA%E5%AD%97%E4%B9%8B%E9%97%B4%E7%A7%BB%E5%8A%A8%E6%95%B0%E6%8D%AE%EF%BC%9F.png)
我有一个日志文件,看起来像这样:
SWEs-elmPCI-A-01(config)#
class AutoQoS-VoIP-RTP-Trust
^
% Invalid input detected at '^' marker.
SWEs-elmPCI-A-01(config)#
class AutoQoS-VoIP-Control-Trust
^
% Invalid input detected at '^' marker.
SWEs-elmPCI-A-01(config)#
policy-map AutoQoS-Police-CiscoPhone
SWEs-elmPCI-A-01(config-pmap)#
现在我必须查找%
包含哈希的行之间以符号开头的所有行#
,并将其放入另一个错误日志文件中。
如果您考虑上述情况,那么这应该进入错误日志文件。
SWEs-elmPCI-A-01(config)#
class AutoQoS-VoIP-RTP-Trust
^
% Invalid input detected at '^' marker.
SWEs-elmPCI-A-01(config)#
class AutoQoS-VoIP-Control-Trust
^
% Invalid input detected at '^' marker.
SWEs-elmPCI-A-01(config)#
答案1
不确定这是否是您想要的,但是:
#!/bin/bash
awk '
BEGIN {
err=0
}
/^SWE.*#$/ {
if (err) {
printf "%s\n%s", txt, $0
txt=""
err=0
} else {
txt=$0
}
next
}
/^% Invalid/ {
err=1
txt=txt "\n" $0
next
}
{
txt=txt "\n" $0
}
END {
print ""
}
' "$1"
结果:
SWEs-elmPCI-A-01(config)#
class AutoQoS-VoIP-RTP-Trust
^
% Invalid input detected at '^' marker.
SWEs-elmPCI-A-01(config)#
class AutoQoS-VoIP-Control-Trust
^
% Invalid input detected at '^' marker.
SWEs-elmPCI-A-01(config)#
作为替代方案,我的想法是使用额外的行进行分组:
awk '
BEGIN {
i = 0
sep="------------------------------------------------"
}
/^% Invalid/ {
printf "%s %3d\n%s\n%s\n%s\n",
sep, ++i, txt, $0, sep
txt=""
next
}
/^SWE.*#$/ {
txt=$0
next
}
txt != "" {
txt=txt "\n" $0
}
' "$1"
结果:
------------------------------------------------ 1
SWEs-elmPCI-A-01(config)#
class AutoQoS-VoIP-RTP-Trust
^
% Invalid input detected at '^' marker.
------------------------------------------------
------------------------------------------------ 2
SWEs-elmPCI-A-01(config)#
class AutoQoS-VoIP-Control-Trust
^
% Invalid input detected at '^' marker.
------------------------------------------------
答案2
也许是这样的:
sed 'H;/#$/!d;s/^/\
/;x;/\n%/!d;s/.//;x;s/.*//;x'
由于您需要错误之前和错误之后的提示,因此该提示包括两者,但不包括在一个错误之后和下一个错误之前都会显示的提示,以匹配示例输出。
如果我们包含两个提示,无论错误是否相互跟随,都会简单得多:
sed 'H;/#$/!d;x;/\n%/!d'
如果您只想要错误之前的提示,那也会简单得多:
sed '/#$/!{H;d;}
x;/\n%/!d'
也许解释这些的最好方法就是翻译成更详细的语言,比如perl
.在 中sed
,保持空间就像一个用空行初始化的静态变量,而模式空间是一个sed
依次分配给每一行输入的变量。就像是:
$hold_space = "\n";
LINE: while ($pattern_space = <>) {
<sed commands go here>
print $pattern_space; # unless -n option is passed
}
该H
命令翻译为:
$hold_space .= $pattern_space;
该d
命令翻译为:
next LINE;
即停止执行命令,丢弃模式空间并从下一行输入重新开始。
x
交换模式和保持空间:
($pattern_space, $hold_space) = ($hold_space, $pattern_space);
所以最后一个sed
命令翻译为:
$hold_space = "\n";
LINE: while ($pattern_space = <>) {
if (! ($pattern_space =~ /#$/)) { # anything other than a prompt
$hold_space .= $pattern_space;
next LINE;
}
($pattern_space, $hold_space) = ($hold_space, $pattern_space);
if (! ($pattern_space =~ /\n%/)) {
next LINE;
}
print $pattern_space;
}
可以用更易读的方式重写为:
LINE: while ($pattern_space = <>) {
if ($pattern_space =~ /#$/)) {
if ($hold_space =~ /\n%/)) {
print $hold_space;
}
$hold_space = $pattern_space;
} else {
$hold_space .= $pattern_space;
}
}
重命名$pattern_space
为$line
和$hold_space
to $current_block
,它会变得更加清晰。