Bash 合并变量

Bash merge variable

我正在尝试获取有关 VPN 连接的状态信息。 几乎完成了,除了 return 实际 PH2 名称的部分:

第 1 步

#!/bin/bash
OLDIFS=$IFS
export IFS=`/bin/echo -ne " \t\n"`
VPN="VPN-AAA-BBB-1"
ph2table=$(snmpwalk 192.168.1.1 -c public -v2c fgVpnTunEntPhase2Name -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib | sed "s/FORTINET-FORTIGATE-MIB:://" | sed "s/STRING: //" | grep $VPN)

$ph2table 现在包含:

fgVpnTunEntPhase2Name.71 = VPN-AAA-BBB-1-P2-0.1
fgVpnTunEntPhase2Name.72 = VPN-AAA-BBB-1-P2-0.2
fgVpnTunEntPhase2Name.73 = VPN-AAA-BBB-1-P2-0.3
fgVpnTunEntPhase2Name.74 = VPN-AAA-BBB-1-P2-0.4
fgVpnTunEntPhase2Name.75 = VPN-AAA-BBB-1-P2-1.5
fgVpnTunEntPhase2Name.76 = VPN-AAA-BBB-1-P2-1.6
fgVpnTunEntPhase2Name.77 = VPN-AAA-BBB-1-P2-1.7
fgVpnTunEntPhase2Name.78 = VPN-AAA-BBB-1-P2-1.8
fgVpnTunEntPhase2Name.79 = VPN-AAA-BBB-1-P2-2.9
fgVpnTunEntPhase2Name.80 = VPN-AAA-BBB-1-P2-3.10

第 2 步

clean=$(echo $ph2table | sed 's/fgVpnTunEntPhase2Name/fgVpnTunEntStatus/' | sed 's/=.*//')

$clean 现在包含:

fgVpnTunEntStatus.71
fgVpnTunEntStatus.72
fgVpnTunEntStatus.73
fgVpnTunEntStatus.74
fgVpnTunEntStatus.75
fgVpnTunEntStatus.76
fgVpnTunEntStatus.77
fgVpnTunEntStatus.78
fgVpnTunEntStatus.79
fgVpnTunEntStatus.80

步骤 3

export IFS=$OLDIFS

for i in "$clean" ; do snmpget 192.168.1.1 -c public -v2c $i -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib ; done | sed "s/FORTINET-FORTIGATE-MIB::fgVpnTunEntStatus.//" | sed 's/= INTEGER: //'

这将 return:

71 up(2)
72 down(1)
73 up(2)
74 up(2)
75 up(2)
76 up(2)
77 up(2)
78 up(2)
79 up(2)
80 up(2)

我想要得到的最终输出是:

VPN-AAA-BBB-1-P2-0.1 71 up(2)
VPN-AAA-BBB-1-P2-0.2 72 down(1)
VPN-AAA-BBB-1-P2-0.3 73 up(2)
VPN-AAA-BBB-1-P2-0.4 74 up(2)
VPN-AAA-BBB-1-P2-1.5 75 up(2)
VPN-AAA-BBB-1-P2-1.6 76 up(2)
VPN-AAA-BBB-1-P2-1.7 77 up(2)
VPN-AAA-BBB-1-P2-1.8 78 up(2)
VPN-AAA-BBB-1-P2-2.9 79 up(2)
VPN-AAA-BBB-1-P2-3.10 80 up(2)

我似乎找不到将所有内容合并在一起的方法。关于这个有什么想法吗?

如果线条肯定匹配,您可以将它们与 paste(1) 组合起来。

我认为最简单的方法是退后一步,即时执行此操作:

while read ph; do
  # clean is only a single cleaned entry now
  clean=$(echo "$ph" | sed 's/fgVpnTunEntPhase2Name/fgVpnTunEntStatus/;s/ =.*//')

  # status for that one entry
  status=$(snmpget 192.168.1.1 -c public -v2c "$clean" -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib | sed 's/FORTINET-FORTIGATE-MIB::fgVpnTunEntStatus.//; s/= INTEGER: //')

  # isolate VPN-AAA-BBB-1-P2-x.y part
  cur=$(echo "$ph" | sed 's/.* = //')

  # print the stuff we want.
  echo "$cur $status"

# instead of feeding "$phtable2" to the loop, it would also be possible to
# pipe the output of the command that was put there directly through the loop,
# as in command | while read ph; do ... done
done <<< "$phtable2"

该代码必然未经测试,因为我没有您的 SNMP 设置,但我希望从中可以清楚地了解总体思路。

我还会查看 snmp 工具的文档,看看是否有比通过 sed 将其全部通过管道传输更直接的隔离变量值的方法。如果工具更新并且它们的输出格式略有变化,这可能会成为一个问题。

在变量中捕获内容并不是经验丰富的 shell 脚本程序员通常采用的方式。只是让一切都在管道中。

这真的很简单,但我想解释为什么我改变了一些东西,所以下面包含一个注释版本。

#!/bin/bash

VPN="VPN-AAA-BBB-1"

snmpwalk 192.168.1.1 -c public -v2c fgVpnTunEntPhase2Name \
    -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib |
sed -e 's/FORTINET-FORTIGATE-MIB:://' -e 's/STRING: //' -e "/$VPN/!d" |
while read i _ id; do
    snmpget 192.168.1.1 -c public -v2c "$i" \
        -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib |
    sed -e 's/FORTINET-FORTIGATE-MIB::fgVpnTunEntStatus.//' -e 's/= INTEGER: //' \
        -e "s/^/$id /"
done

(这里没有任何 Bash 特定的内容,因此您实际上可以将 shebang 更改为 #!/bin/sh。这在这里并不重要,但如果您需要 运行这在 Bash 太大、太慢或不可用的系统上,使用 Dash 的选项可能很有吸引力。)

带注释的版本有点冗长,并且有一个讨厌的垂直滚动条,但注释解释了我更改了什么以及为什么。

#!/bin/bash
#OLDIFS=$IFS
#export IFS=`/bin/echo -ne " \t\n"`
# No need to export (you don't want or need this in subprocesses)
# No need to echo (bash has adequate internal facilities for representing strings)
# IFS=$' \t\n'
# Commented out because we do the required parsing in sed instead

VPN="VPN-AAA-BBB-1"

#ph2table=$(snmpwalk 192.168.1.1 -c public -v2c fgVpnTunEntPhase2Name \
#-m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib |
#sed "s/FORTINET-FORTIGATE-MIB:://" | sed "s/STRING: //" | grep $VPN)
# No need to use multiple processes; sed can do everything in one go
# No need for a variable -- feed this into a pipeline
snmpwalk 192.168.1.1 -c public -v2c fgVpnTunEntPhase2Name \
    -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib |
sed -e 's/FORTINET-FORTIGATE-MIB:://' -e 's/STRING: //' -e "/$VPN/!d" |

# The following could be added to replace "clean" but we take a different approach
#    -e 's/fgVpnTunEntPhase2Name/fgVpnTunEntStatus/' -e 's/=.*//' |

#clean=$(echo $ph2table | sed 's/fgVpnTunEntPhase2Name/fgVpnTunEntStatus/' |
#sed 's/=.*//')|
# No need for a variable
# $ph2table absolutely needs to be quoted here -- "$ph2table"
# but we don't want to do this -- instead, keep this in the output

#export IFS=$OLDIFS
# No longer necessary -- see above

#for i in "$clean" ; do
# Instead of a for loop over a variable, do a while loop over output lines from pipe
# Split into i, equals sign (in $_, which we ignore), and id
while read i _ id; do

    #snmpget 192.168.1.1 -c public -v2c $i \
    #-m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib ; done |
    # Definitely need to quote "$i"

    snmpget 192.168.1.1 -c public -v2c "$i" \
        -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib |

    #sed "s/FORTINET-FORTIGATE-MIB::fgVpnTunEntStatus.//" |
    # sed 's/= INTEGER: //'
    # Again, use a single sed invocation
    # Factor this inside the while loop, and add the id to the output
    sed -e 's/FORTINET-FORTIGATE-MIB::fgVpnTunEntStatus.//' \
        -e 's/= INTEGER: //' \
        -e "s/^/$id /"

done

我没有测试它,但像这样的东西应该可以工作:

VPN="VPN-AAA-BBB-1" && while read -ra myarr; do
snmp=$(snmpget 192.168.1.1 -c public -v2c "${myarr[0]}" -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib ; done | sed "s/FORTINET-FORTIGATE-MIB::fgVpnTunEntStatus.//" | sed 's/= INTEGER: //')
echo "${myarr[0]} ${snmp}"
done < <(snmpwalk 192.168.1.1 -c public -v2c fgVpnTunEntPhase2Name -m /usr/share/snmp/mibs/FORTINET-FORTIGATE-MIB.mib | sed "s/FORTINET-FORTIGATE-MIB:://" | sed "s/STRING: //" | grep "${VPN}") 

这避免了使用 IFS,如果您的代码在设置 IFS 时退出(不是捕获错误),您可能会像您所说的那样出现不可预知的行为。