awk 无法处理 sed 的管道输出

Rah*_*waj 5 zsh pipe sed awk

我有一个日志文件,大致如下所示:

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 的原因;


sch*_*ity 8

根据手册

\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
\n

所以在你的情况下你可以逃避美元符号$

\n
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print \\$1}"\n
Run Code Online (Sandbox Code Playgroud)\n

但使用单引号更容易:

\n
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F \' ; \' \'{ print $1 }\'\n
Run Code Online (Sandbox Code Playgroud)\n

您还可以在分隔符之间留出空格\' ; \',这样每行之后就不会以不可见的空格结束。

\n

您也可以只使用awk

\n
sed -u -n -e "s/^.*cdsmon: //p" /tmp/dev.log | awk -F ";" "{print \\$1}"\n
Run Code Online (Sandbox Code Playgroud)\n


roa*_*ima 8

我会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)

您没有说明要如何提取实例和原因(或者如果您这样做了,我错过了),所以我只是将它们打印在字符串中,证明它们已被正确提取。

  • 使用`-F'不是更容易吗: | ; '` 作为字段分隔符? (2认同)