我有一个日志文件,大致如下所示:
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
Run Code Online (Sandbox Code Playgroud)
我想检测 的行cdsmon
,然后将行分割;
(以获取/tmp/instance0
和 类似的事件core dumped
)。
为此我用作sed
:
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log
Run Code Online (Sandbox Code Playgroud)
其输出为:
/tmp/instance0 ; core dumped
/tmp/instance0 ; core dumped
/tmp/instance1 ; core dumped
/tmp/instance2 ; core dumped
Run Code Online (Sandbox Code Playgroud)
但是,将此输出通过管道传输到awk
如下所示时,它会给出与上面相同的输出:
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print $1}"
Run Code Online (Sandbox Code Playgroud)
-u
尽管从 中删除了该选项,但还是观察到了同样的情况sed
。
如果我遗漏了什么,有人可以指出吗?我正在使用带有常规 awk/sed 的 FreeBSD 盒子,不幸的是无法安装任何新软件包。
Adm*_*Bee 19
行为的原因awk
是您将程序用双引号引起来,这使得字符串对 shell 的变量扩展开放。这意味着运行程序的 shell 将首先展开$1
,并且由于这可能是未定义的,因此它会展开为空字符串。
所以,你的程序相当于
awk -F ";" "{print}"
Run Code Online (Sandbox Code Playgroud)
这就是打印整行的原因。awk
这是您应该始终将( 和sed
) 程序包含在单引号中的原因之一。
请注意,在大多数情况下,您不需要将输出通过sed
管道传输到awk
,反之亦然。在您的示例中,如果您想获取“事件标签”之后的第一个字段,您可以执行以下操作:
sed -E -n 's/^.*cdsmon: ([^;]*).*$/\1/p' /tmp/dev.log
Run Code Online (Sandbox Code Playgroud)
这将在第一个之后的字符串周围定义一个捕获组,并将整行替换为该捕获组的内容。cdsmon:
;
如果您想打印 记录的事件的摘要cdsmon
,您可以将sed
上述方法扩展为:
sed -E -n 's/^.*cdsmon: ([^;]*) ; (.*)$/\1 : \2/p' dev.log
Run Code Online (Sandbox Code Playgroud)
或者,这是另一种awk
-only 方法:
awk -F'(cdsmon: | ; )' 'NF==3{printf "%s : %s\n",$2,$3}' dev.log
Run Code Online (Sandbox Code Playgroud)
对于您的示例,两者都会打印
/tmp/instance0 : core dumped
/tmp/instance0 : core dumped
/tmp/instance1 : core dumped
/tmp/instance2 : core dumped
Run Code Online (Sandbox Code Playgroud)
但请注意,该awk
方法可能会遇到边缘情况。它采用模式cdsmon:
和;
作为字段分隔符。当有三个字段时(在您的示例中,它只能发生在条目中cdsmon:
),它会打印第二个和第三个字段,对应于 after 的实例名称cdsmon:
和 after 的原因;
。
根据手册:
\n\n\n双引号保护左引号和右引号之间的大部分内容。\n shell 至少对引用的文本进行变量和命令替换。不同的 shell 可能会对双引号文本执行其他类型的\n处理。
\n由于双引号文本中的某些字符是由 shell 处理的,因此必须在文本中对它们进行转义。值得注意的是\n字符 \xe2\x80\x98$\xe2\x80\x99、\xe2\x80\x98`\xe2\x80\x99、\xe2\x80\x98\\\xe2\x80\x99 和\xe2\x80\x98"\xe2\x80\x99,如果要将它们按字面意思传递给程序,则所有这些都必须在双引号文本中前面有一个反斜杠。
\n
所以在你的情况下你可以逃避美元符号$
:
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print \\$1}"\n
Run Code Online (Sandbox Code Playgroud)\n但使用单引号更容易:
\nsed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F \' ; \' \'{ print $1 }\'\n
Run Code Online (Sandbox Code Playgroud)\n您还可以在分隔符之间留出空格\' ; \'
,这样每行之后就不会以不可见的空格结束。
您也可以只使用awk
:
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print \\$1}"\n
Run Code Online (Sandbox Code Playgroud)\n
我会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
Run Code Online (Sandbox Code Playgroud)
这是评论中建议的替代且更简单的解决方案,我们在冒号或分号上进行分割,因此必要的字段已经直接在awk
变量中:
awk -F': | *; *' '
$1 ~ / cdsmon$/ { printf "instance %s, reason %s\n", $2, $3 }
' /tmp/dev.log
Run Code Online (Sandbox Code Playgroud)
您没有说明要如何提取实例和原因(或者如果您这样做了,我错过了),所以我只是将它们打印在字符串中,证明它们已被正确提取。