在Bash脚本中使用带引号和空格的awk

4

我从一个接收到的SNMP陷阱中设置了一个bash变量,并获得以下输出:

echo $var

Nov 27 16:20:34 witness logger: trap: vars: DISMAN-EVENT-MIB::sysUpTimeInstance = 0:6:10:29.06,  SNMPv2-MIB::snmpTrapOID.0 = SNMPv2-SMI::enterprises.11.2.29.2.90.0.10000002, SNMPv2 SMI::enterprises.11.2.29.2.90.1 = "Finished Number", SNMPv2-SMI::enterprises.11.2.29.2.90.2 = "Filter Cirteria: [called='3333']", SNMPv2-SMI::enterprises.11.2.29.2.90.3 = "Cleared", SNMPv2     SMI::enterprises.11.2.29.2.90.4 = "major Over-Flow alert on Finished Number for ['3333']", SNMPv2 SMI::enterprises.11.2.29.2.90.5 = "The Corresponding Metric Value is: 0.5", SNMPv2- SMI::enterprises.11.2.29.2.90.6 = "Over-Flow", SNMPv2-SMI::enterprises.11.2.29.2.90.7 = "Tue Nov 27 16:20:05 CET 2012" 

我正在尝试将以下输出存储在变量中:

var1 = "Tue Nov 27 16:20:05 CET 2012"
var2 = "Finished Number"
var3 = "The Corresponding Metric Value is: 0.5"
var4 = "Cleared"
var5 = "major Over-Flow alert on Finished Number for ['3333']"

我正在考虑通过awk来完成这个任务,根据snmp OID:enterprises.11.2.29.2.90.4,enterprises.11.2.29.2.90.5,11.2.29.2.90.6等等来执行。但是似乎无法提取引号中的内容“”。
5个回答

3

Perl解决方案:

echo "$var" | perl -nE 'say "var", ++$x, "=$1" while /(".*?")/g'

输出:

var1="Finished Number"
var2="Filter Cirteria: [called='3333']"
var3="Cleared"
var4="major Over-Flow alert on Finished Number for ['3333']"
var5="The Corresponding Metric Value is: 0.5"
var6="Over-Flow"
var7="Tue Nov 27 16:20:05 CET 2012"

1
我认为我们应该花点时间感谢 Perl 如此出色。 - lynks
1
然后再花一点时间将脚本输出与期望的输出进行比较 :-)。 - Ed Morton
@EdMorton:我只是想帮忙。添加var2=$var1; var1=$var7; var6=$var5; var5=$var4; var4=$var3; var3=$var6这一部分留给读者自己完成。 - choroba
我了解,这是一个完全合理、有用的部分解决方案。当@lynks赞扬perl时,发布的输出与更简洁的 grep -o'“[^”]*”' 产生的输出的有用程度相同,当然,您也可以使用类似的awk或ruby脚本生成相同的输出(例如,gawk -v FPAT='"[^"]*"' '{for (i=1;i<=NF;i++) print "var"i"="$i}'),因此,虽然perl是一个有用的答案,在这种情况下,perl并没有为特定情况带来独特的东西。 - Ed Morton

3

看起来你想匹配双引号内的所有字符串,这可以通过使用grep最简单地完成:

$ echo $var | grep -o '"[^"]*"'

"Finished Number"
"Filter Cirteria: [called=3333]"
"Cleared"
"major Over-Flow alert on Finished Number for [3333]"
"The Corresponding Metric Value is: 0.5"
"Over-Flow"
"Tue Nov 27 16:20:05 CET 2012"

说明:

-o 参数只打印匹配的行的部分内容。

"     # Match opening double quote
[^"]* # Match anything not a double quote
"     # Match closing double quote

希望这能帮助你入门。

2

让我们从一些简单的内容开始,这样你就可以看到当你使用 awk 时,字段会如何被分解:

echo "${var}" | awk 'BEGIN{FS="\""} {for (i=1; i<=NF; i++) {print "["i"]", $i}}'

如果您的shell支持herestrings:

awk 'BEGIN{FS="\""} {for (i=1; i<=NF; i++) {print "["i"]", $i}}' <<< "${var}"

输出:

[1] Nov 27 16:20:34 witness logger: trap: vars: DISMAN-EVENT-MIB::sysUpTimeInstance = 0:6:10:29.06,  SNMPv2-MIB::snmpTrapOID.0 = SNMPv2-SMI::enterprises.11.2.29.2.90.0.10000002, SNMPv2 SMI::enterprises.11.2.29.2.90.1 = 
[2] Finished Number
[3] , SNMPv2-SMI::enterprises.11.2.29.2.90.2 = 
[4] Filter Cirteria: [called='3333']
[5] , SNMPv2-SMI::enterprises.11.2.29.2.90.3 = 
[6] Cleared
[7] , SNMPv2     SMI::enterprises.11.2.29.2.90.4 = 
[8] major Over-Flow alert on Finished Number for ['3333']
[9] , SNMPv2 SMI::enterprises.11.2.29.2.90.5 = 
[10] The Corresponding Metric Value is: 0.5
[11] , SNMPv2- SMI::enterprises.11.2.29.2.90.6 = 
[12] Over-Flow
[13] , SNMPv2-SMI::enterprises.11.2.29.2.90.7 = 
[14] Tue Nov 27 16:20:05 CET 2012
[15]  

现在根据需要选择字段:

var1=$(awk 'BEGIN{FS="\""} {print $14}' <<< "${var}")
var2=$(awk 'BEGIN{FS="\""} {print $2}' <<< "${var}")
var3=$(awk 'BEGIN{FS="\""} {print $10}' <<< "${var}")
var4=$(awk 'BEGIN{FS="\""} {print $6}' <<< "${var}")
var5=$(awk 'BEGIN{FS="\""} {print $8}' <<< "${var}")

说明:

  • awk 'BEGIN{FS="\""}:在这里我们使用 awk 命令以" 为分隔符来处理输入
  • {print $14}':打印被引号包含的特定字段
  • <<< "${var}":如果可用,使用 herestring 而不是 echo 命令(见上文)
  • 这是在假设您的 $var 格式在字段顺序方面保持相对一致的情况下进行的

1
$ echo "$var" | awk -F\" 'BEGIN{n=split("14 2 10 6 8",v," ")} {for (i=1;i<=n;i++) printf "var%d = \"%s\"\n",i,$(v[i])}'
var1 = "Tue Nov 27 16:20:05 CET 2012"
var2 = "Finished Number"
var3 = "The Corresponding Metric Value is: 0.5"
var4 = "Cleared"
var5 = "major Over-Flow alert on Finished Number for ['3333']"

另外,也许更符合您的需求,以下是如何使用awk执行结果填充shell数组的方法:

$ IFS=$'\n' varArr=( $(echo "$var" | awk -F\" 'BEGIN{n=split("14 2 10 6 8",v," ")}
 {for (i=0;i<=n;i++) printf "\"%s\"\n",$(v[i])}') )

$ echo "${varArr[1]}"                                                         
"Tue Nov 27 16:20:05 CET 2012"

$ echo "${varArr[2]}"
"Finished Number"

$ echo "${varArr[3]}"
"The Corresponding Metric Value is: 0.5"

$ echo "${varArr[4]}"
"Cleared"

$ echo "${varArr[5]}"
"major Over-Flow alert on Finished Number for ['3333']"

如果您不想在文本周围添加引号,只需在awk脚本中不添加它们:

IFS=$'\n' varArr=( $(echo "$var" | awk -F\" 'BEGIN{n=split("14 2 10 6 8",v," ")}
{for (i=0;i<=n;i++) print $(v[i])}') )

以上两种方法都会将整个输入字符串放入${varArr [0]}中。如果不想这样,这是一个微不足道的调整。


0

最终我使用了awk的解决方案,但其他方案也很适合。感谢大家。

val=$(echo $val |  awk '{for(i=1;i<=NF;i++)if($i~/is:/)print $(i+1)}' | cut -d\" -f 1)

关于脚本的信息:当 snmptrapd 接收到陷阱时,它会将日志放入消息中,并对特定警报执行其他操作。

主循环如下:

vars=
while read oid val
do
if [ "$vars" = "" ]
  then
    vars="$oid = $val"
  else
    vars="$vars, $oid = $val" 
        if [ "$oid" == "SNMPv2-SMI::enterprises.11.2.29.2.90.5" ]
        then
          val=$(echo $val |  awk '{for(i=1;i<=NF;i++)if($i~/is:/)print $(i+1)}' | cut -d\" -f 1)
          /bin/logger "found: value 5:    $val "
          val5=$val
        fi
 fi
done

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接