我有一个日志文件,大致如下所示:
Sep 23 10:28:26 node kernel: em0: device is going DOWN
Sep 23 10:28:26 node kernel: em0: device is going UP
Sep 23 10:29:14 node cdsmon: /tmp/instance0 ; core dumped
Sep 23 10:29:14 node cdsmon: /tmp/instance0 ; core dumped
Sep 23 10:28:26 node kernel: em0: device is going DOWN
Sep 23 10:29:14 node cdsmon: /tmp/instance1 ; core dumped
Sep 23 10:28:26 node kernel: em0: device is going UP
Sep 23 10:29:14 node cdsmon: /tmp/instance2 ; core dumped
我想检测 的行,cdsmon
然后将行分割;
(以获取/tmp/instance0
和 类似的事件core dumped
)。
为此我用作sed
:
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log
其输出为:
/tmp/instance0 ; core dumped
/tmp/instance0 ; core dumped
/tmp/instance1 ; core dumped
/tmp/instance2 ; core dumped
但是,将此输出通过管道传输到awk
如下所示时,它会给出与上面相同的输出:
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print $1}"
-u
尽管从 中删除了该选项,但还是观察到了同样的情况sed
。
如果我遗漏了什么,有人可以指出吗?我正在使用带有常规 awk/sed 的 FreeBSD 盒子,不幸的是无法安装任何新软件包。
答案1
的行为的原因awk
是您已将程序包含在双倍的引号,使字符串对 shell 的变量扩展开放。这意味着运行程序的 shell 将首先展开$1
,并且由于这可能是未定义的,因此它会展开为空字符串。
所以,你的程序相当于
awk -F ";" "{print}"
这就是打印整行的原因。这是您应该始终将您的awk
(和sed
)程序包含在其中的原因之一单身的引号。
请注意,在大多数情况下,您不需要将输出通过管道传输sed
到awk
,反之亦然。在您的示例中,如果您想获取“事件标签”之后的第一个字段,您可以执行以下操作:
sed -E -n 's/^.*cdsmon: ([^;]*).*$/\1/p' /tmp/dev.log
这将定义一个捕获组cdsmon:
围绕第一个之后的字符串;
,并将整行替换为该捕获组的内容。
如果您想打印 记录的事件的摘要cdsmon
,您可以将sed
上述方法扩展为:
sed -E -n 's/^.*cdsmon: ([^;]*) ; (.*)$/\1 : \2/p' dev.log
或者,这是另一种awk
-only 方法:
awk -F'(cdsmon: | ; )' 'NF==3{printf "%s : %s\n",$2,$3}' dev.log
对于您的示例,两者都会打印
/tmp/instance0 : core dumped
/tmp/instance0 : core dumped
/tmp/instance1 : core dumped
/tmp/instance2 : core dumped
但请注意,该awk
方法可能会遇到边缘情况。它采用模式cdsmon:
和;
作为字段分隔符。当有三个字段时(在您的示例中,它只能发生在条目中cdsmon:
),它会打印第二个和第三个字段,对应于 after 的实例名称cdsmon:
和 after 的原因;
。
答案2
我会awk
在整个操作中使用。在这里,我按冒号进行分割,因此在考虑日期/时间后,必须将主机匹配应用于第三个字段(14 node cdsmon
例如):
awk -F: '
$3 ~ / cdsmon$/ {
split($4, text, / *; */); # Split field at semicolon
sub(/^ */, "", text[1]); # Remove leading space
printf "instance %s, reason %s\n", text[1], text[2]
}
' /tmp/dev.log
这是在中建议的替代且更简单的解决方案评论,我们用冒号或分号进行分割,因此必要的字段已经直接在awk
变量中:
awk -F': | *; *' '
$1 ~ / cdsmon$/ { printf "instance %s, reason %s\n", $2, $3 }
' /tmp/dev.log
您没有说明要如何提取实例和原因(或者如果您这样做了,我错过了),所以我只是将它们打印在字符串中,证明它们已被正确提取。
答案3
双引号可以保护开盘价和收盘价之间的大部分内容。 shell 至少对引用的文本进行变量和命令替换。不同的 shell 可能会对双引号文本进行其他类型的处理。
由于双引号文本中的某些字符由 shell 处理,因此必须在文本中对它们进行转义。值得注意的是字符 '$'、'``'、'\' 和 '"',如果要按字面传递给程序,所有这些字符都必须在双引号文本中前面有一个反斜杠。
所以在你的情况下你可以逃避美元符号$
:
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print \$1}"
但使用单引号更容易:
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ' ; ' '{ print $1 }'
您还可以在分隔符之间留出空格,' ; '
这样每行后面就不会出现不可见的空格。
您也可以只使用awk
:
$ awk -F': | ; ' '/cdsmon/ { print $2 }' /tmp/dev.log
/tmp/instance0
/tmp/instance0
/tmp/instance1
/tmp/instance2
答案4
awk '{for(i=1;i<=NF;i++){if($i ~ /cdsmon/){print $(i+1),$(i+3),$(i+4)}}}' filename
输出
/tmp/instance0 core dumped
/tmp/instance0 core dumped
/tmp/instance1 core dumped
/tmp/instance2 core dumped
awk '{for(i=1;i<=NF;i++){if($i ~ /cdsmon/){print $(i+1)}}}' filename
/tmp/instance0
/tmp/instance0
/tmp/instance1
/tmp/instance2