简体   繁体   English

BASH在循环中搜索数组以查找多个字符串

[英]BASH Searching array for multiple strings in a loop

Hello all and thanks for reading, 大家好,感谢您的阅读,

I am trying to write a script that will parse through Cisco Configs and determine which interfaces are or not in a specific vlan and if they are shutdown or not. 我正在尝试编写一个脚本,该脚本将通过Cisco Configs进行解析,并确定哪些接口位于特定VLAN中,以及它们是否处于关闭状态。 I thought it would be easy enough to parse through the array and search for the items that I was looking for and set a varialble for them I have run into some problems. 我认为解析数组并搜索我要寻找的项目并为它们设置变量会很容易,因为我遇到了一些问题。 Below is the basic part of the script, this reads the file into the array and the echo just dumps the array. 以下是脚本的基本部分,它将文件读取到数组中,而echo仅将数组转储。 What I am looking for is a way to read the array for three things: 我正在寻找一种读取数组的三种方法:

  • interface name 接口名称
  • switchport access vlan 交换端口访问VLAN
  • shutdown 关掉

The basic flow is that the script (interfaces.sh) reads in any *.cfg file and reads the interfaces in as an array. 基本流程是脚本(interfaces.sh)读取任何* .cfg文件,并以数组形式读取接口。 At this point it parses through the arrays searching for those fields. 此时,它将解析数组以搜索那些字段。 If found, set a value to be used later. 如果找到,请设置一个以后使用的值。 At the end, it takes the values for VLAN and isShut and based on their values, it reports if the interface is in vlan 2 and not shutdown or in another vlan and shut. 最后,它获取VLAN和isShut的值,并基于它们的值,报告接口是在vlan 2中而不是关闭还是在另一个vlan中并关闭。

Basic Code to get the interfaces into an array 将接口放入数组的基本代码

  ##  BEGIN interfaces.sh ##
  #!/bin/bash
  clear
  ls *.cfg | while read config; do
  IFS=$'\r\n' interfaces=(`sed -n '/^interface/,/!/p' $config `)
  tLen=${#interfaces[@]}
  printf "\b Starting $config... \n"    
  for (( i=0; i<${tLen}; i++ ))
   do
    echo "${interfaces[$i]}" 
   done

  printf "\n\n"
  done

One of the attempts I made 我做过的尝试之一

 #!/bin/bash
 clear
 ls *.cfg | while read config; do
 IFS=$'\r\n' interfaces=(`sed -n '/^interface/,/!/p' $config `)
 tLen=${#interfaces[@]}
 printf "\b Starting $config... \n"
 isInt=0
 isShut=0
 VLAN=0

 for (( i=0; i<${tLen}; i++ ))
 do
         if [[ $(echo "${interfaces[$i]}" | grep interface | grep net) ]]; then
                 int_name=${interfaces[$i]}
                 isInt=1
         fi

         if [[ $(echo "${interfaces[$i]}" | grep "access vlan" | grep -v "access vlan 2$") ]]; then
            VLAN="vlan1"
         fi

    if [[ $(echo "${interfaces[$i]}" | grep "access vlan 2$") ]]; then
                VLAN="vlan2"
    fi

         if [[ $(echo "${interfaces[$i]}" | grep -v " shutdown$") ]]; then
            isShut="notShutdown"
         fi

         if [[ $(echo "${interfaces[$i]}" | grep " shutdown$") ]]; then
                 isShut="shut"
    fi



    # This put here to test if the variables vlan and isShut is being set.
    # IF you uncomment this line you can see that the values are set then 
    # on the next pass it is changed of some of the values.  I dont know
    # how to fix this.
    #echo " $int_name vlan=$VLAN isShut=$isShut"

    # Below is the results of the value changing
    # interface Ethernet2/3 vlan=vlan1 isShut=notShutdown
    # interface Ethernet2/3 vlan=vlan2 isShut=notShutdown
    # interface Ethernet2/3 vlan=vlan2 isShut=notShutdown
    # interface Ethernet2/3 vlan=vlan2 isShut=shut
    # interface Ethernet2/3 vlan=vlan2 isShut=notShutdown
    # interface Ethernet2/3 vlan=vlan2 isShut=notShutdown
    # interface Ethernet2/3 is in vlan 2 and is not shutdown


         # End of interface section so reset counters
         if [[ "${interfaces[$i]}" == '!' ]]
           then
                 if [[ "$VLAN" == "vlan1" && "$isShut" == "notShutdown" ]]; then
                         echo "$int_name is NOT in vlan 2 and is not shutdown"
            fi

                 if [[ "$VLAN" == "vlan1" && "$isShut" == "shut" ]]; then
                         echo "$int_name is NOT in vlan 2 and is shutdown"
            fi

                 if [[ "$VLAN" == "vlan2" && "$isShut" == "notShutdown" ]]; then
                         echo "$int_name is in vlan 2 and is not shutdown"
            fi

                 if [[ "$VLAN" == "vlan2" && "$isShut" == "shut" ]]; then
                         echo "$int_name is in vlan 2 and is shutdown"
            fi

    isInt=0
    isShut=0
    vlan=0

         fi
 done

 printf "\n\n"
 done

Begin Cisco Config # 开始Cisco Config#

  # Save this section as config.txt
Current configuration : 2271 bytes
 !
 ! Last configuration change at 18:30:45 CET Fri Jul 25 2014
 !
 version 15.0
 no service pad
 service timestamps debug datetime msec
 service timestamps log datetime msec
 no service password-encryption
 !
 hostname SW1
 !
 boot-start-marker
 boot-end-marker
 !
 !
 enable password cisco
 !
 no aaa new-model
 clock timezone CET 1
 !
 ip cef
 no ip domain-lookup
 !
 ipv6 unicast-routing
 ipv6 cef
 vtp domain CCIE
 vtp mode transparent
 !
 !
 !
 spanning-tree mode pvst
 spanning-tree extend system-id
 !
 vlan internal allocation policy ascending
 !
 vlan 11 
 !
 ! 
 !
 !
 !
 !
 !
 interface Loopback0
  ip address 6.6.7.7 255.255.255.255
 !
 interface Ethernet0/0
  duplex auto
  shutdown
 !
 interface Ethernet0/1
  no switchport
  ip address 6.6.17.7 255.255.255.0
  duplex auto
 !
 interface Ethernet0/2
  duplex auto
 !
 interface Ethernet0/3
  duplex auto
 !
 interface Ethernet1/0
  switchport access vlan 20
  switchport mode access
  duplex auto
 !
 interface Ethernet1/1
  switchport access vlan 5
  switchport mode access
  duplex auto
 !
 interface Ethernet1/2
  switchport access vlan 2
  switchport mode access
  shutdown
  duplex auto
 !
 interface Ethernet1/3
  switchport access vlan 2
  switchport mode access
  duplex auto
 !
 interface Ethernet2/0
  switchport access vlan 2
  switchport mode access
  duplex auto
 !
 interface Ethernet2/1
  switchport access vlan 2
  switchport mode access
  duplex auto
 !
 interface Ethernet2/2
  switchport access vlan 40
  switchport mode access
  duplex auto
 !
 interface Ethernet2/3
  switchport access vlan 2
  switchport mode access
  shutdown
  duplex auto
 !
 interface Ethernet3/0
  switchport access vlan 10
  switchport mode access
  shutdown
  duplex auto
 !
 interface Ethernet3/1
  switchport access vlan 10
  switchport mode access
  shutdown
  duplex auto
 !
 interface Ethernet3/2
  switchport access vlan 10
  switchport mode access
  shutdown
  duplex auto
 !
 interface Ethernet3/3
  switchport access vlan 2
  switchport mode access
  shutdown
  duplex auto
 !
 interface Vlan1
  no ip address
  shutdown
 !
 interface Vlan123
  ip address 6.6.123.7 255.255.255.0
  shutdown
 !
 !
 ip forward-protocol nd
 no ip http server
 !
 !
 !
 !
 !
 control-plane
 !
 !
 line con 0
  exec-timeout 0 0
  privilege level 15
  password cisco
  logging synchronous
 line aux 0
 line vty 0 4
  privilege level 15
  password cisco
  login
  transport input all
 !
 end

I hope I have explained this well enough. 我希望我已经解释得足够好了。 This may be simple for you smarter guys out there but I am struggling with this. 对于您那里的聪明人来说,这可能很简单,但是我为此感到挣扎。

I've written a Python 2.7 script and I've tried to make it robust. 我已经编写了Python 2.7脚本,并且试图使其健壮。 I use an IOS configuration file parser ( ciscoconfparse ) library to avoid bugs in my own parsing attempt. 我使用IOS配置文件解析器( ciscoconfparse )库来避免自己解析尝试中的错误。 I've given it a command line interface so to see the state of interfaces on vlan 2: 我给了它一个命令行界面,以便查看vlan 2上的界面状态:

$ python interfaces.py --vlan 2 /path/to/ios.cfg

Installation 安装

  1. Install Python 2.7 安装Python 2.7
  2. Install cisoconfparse 安装cisoconfparse
  3. Save the script below as some .py (eg, interfaces.py ) 将下面的脚本另存为.py (例如, interfaces.py

interfaces.py interfaces.py

#!/usr/bin/env python

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from argparse import ArgumentParser
from itertools import ifilter
import sys

from ciscoconfparse import CiscoConfParse


INTERFACE_REGEX = 'interface'
SWITCHPORT_REGEX = 'switchport access vlan'


def main(argv=None):
    args = parse_argv(argv=argv)
    parse = CiscoConfParse(args.conf_path)

    objs = parse.find_objects_w_child(INTERFACE_REGEX, SWITCHPORT_REGEX)
    records = (Record.from_ios_object(obj) for obj in objs)
    if args.vlan:
        records = ifilter(lambda r: r.vlan == args.vlan, records)

    for record in sorted(records, key=lambda r: r.name):
        print(record)


def parse_argv(argv=None):
    if argv is None:
        argv = sys.argv
    parser = ArgumentParser()
    parser.add_argument('--vlan', type=int)
    parser.add_argument('conf_path')
    return parser.parse_args(args=argv[1:])


class Record:
    def __init__(self, name, vlan, is_shutdown):
        self.name = name
        self.vlan = vlan
        self.is_shutdown = is_shutdown

    def __str__(self):
        if self.is_shutdown:
            state = 'shutdown'
        else:
            state = 'running'
        return '{name} {vlan} {state}'.format(
            name=self.name,
            vlan=self.vlan,
            state=state
        )

    @classmethod
    def from_ios_object(cls, obj):
        tokens = obj.text.split()
        if len(tokens) != 2:
            raise ValueError('Expected 2 tokens, found ' + len(tokens))
        name = tokens[1]
        children = obj.re_search_children(SWITCHPORT_REGEX)
        if len(children) != 1:
            raise ValueError('Expected 1 matching child, found ' +
                             len(children))
        vlan = int(children[0].re_match('(\d+)$'))
        is_shutdown = bool(obj.re_search_children('shutdown'))
        return cls(name, vlan, is_shutdown)


if __name__ == '__main__':
    sys.exit(main())

Storing each line of the input as a separate element in the array has complicated the processing. 将输入的每一行作为单独的元素存储在数组中会使处理复杂化。 If you merge all of the interface definition into one array element it will simplify your lookups to simple string searches. 如果将所有接口定义合并到一个数组元素中,它将使您的查找简化为简单的字符串搜索。 An AWK mini-state-machine is one way to do this. AWK微型状态机是实现此目的的一种方法。

#!/bin/bash
ls *.cfg | while read config; do
    interfaces=()
    IFS=$'\n'       # We'll delimit the records with this
    count=0
    printf "\b Starting $config... \n"    
    for i in $(awk '# ^interface = start of interface def; insert delimiter, set flag.
                    /^interface Ethernet/{inside_int=1}

                    # ! = end of interface def; unset flag.
                    (/!/ && inside_int){inside_int=0; print}

                    # if flag is set, print line
                    inside_int{printf "%s ", $0}

                    ' config.cfg ); do

        # append to interfaces array
        interfaces=(${interfaces[@]} $i)

        # Create three arrays with interface data

        # Interface name
        intname[$count]=$( echo "$i}" | sed -n "s/interface \([^ ]*\).*/\1/p" )

        # Interface VLAN
        vlan[$count]=$( echo "$i}" | sed -n 's/interface.*switchport access vlan \([^ ]*\).*/\1/p' )

        # Interface up/down (0/1) status
        isdown[$count]=$( echo "$i}" | grep -c shutdown )

        ((count++))
    done

    # Loop and display values.
    for (( i=0; i<${#interfaces[@]}; i++ )); do
        echo -e "Int:${intname[$i]}\tvlan:${vlan[$i]}\tisdown:${isdown[$i]}"
    done
done

The ${intname[@]} , ${vlan[@]} , and ${isdown[@]} arrays contain the individual values you were looking for. ${intname[@]}${vlan[@]}${isdown[@]}数组包含您要查找的单个值。 The ${interfaces[@]} array contains the each interface definition as a separate element that can be string-searched for other data. ${interfaces[@]}数组包含每个接口定义作为一个单独的元素,可以对它进行字符串搜索以查找其他数据。

You shouldn't try to parse hierarchical text files (like a Cisco IOS configuration) with bash scripts... use a canned configuration parsing library... 您不应该尝试使用bash脚本解析分层文本文件(例如Cisco IOS配置)...使用罐头配置解析库...

Improving on @PeterSutton's answer... CiscoConfParse supports automated vlan parsing if you parse with factory=True (yes, I know this is still undocumented, but you can find all the possible parsing values in the source of ciscoconfparse/models_cisco.py )... 改进@PeterSutton的答案...如果您使用factory=True解析, CiscoConfParse支持自动vlan解析(是的,我知道这仍然没有ciscoconfparse/models_cisco.py ,但是您可以在ciscoconfparse/models_cisco.py的源中找到所有可能的解析值)。 ..

The code for parsing out interface name, switchport status and access vlan number is this simple... 解析接口名称,交换端口状态和访问VLAN编号的代码很简单...

from ciscoconfparse import CiscoConfParse
from prettytable import PrettyTable

parse = CiscoConfParse('config.text', factory=True)
table = PrettyTable(['Intf Name', 'Switchport?', 'Access Vlan (0 is no vlan)'])
for intf_obj in parse.find_objects('^interface'):
    table.add_row([intf_obj.name, intf_obj.is_switchport, intf_obj.access_vlan])

print table

When you run that, you get a text table... 运行该命令时,您将获得一个文本表...

(py27_test)[mpenning@tsunami ~]$ python example.py 
+-------------+-------------+----------------------------+
|  Intf Name  | Switchport? | Access Vlan (0 is no vlan) |
+-------------+-------------+----------------------------+
|  Loopback0  |    False    |             0              |
| Ethernet0/0 |    False    |             0              |
| Ethernet0/1 |    False    |             0              |
| Ethernet0/2 |    False    |             0              |
| Ethernet0/3 |    False    |             0              |
| Ethernet1/0 |     True    |             20             |
| Ethernet1/1 |     True    |             5              |
| Ethernet1/2 |     True    |             2              |
| Ethernet1/3 |     True    |             2              |
| Ethernet2/0 |     True    |             2              |
| Ethernet2/1 |     True    |             2              |
| Ethernet2/2 |     True    |             40             |
| Ethernet2/3 |     True    |             2              |
| Ethernet3/0 |     True    |             10             |
| Ethernet3/1 |     True    |             10             |
| Ethernet3/2 |     True    |             10             |
| Ethernet3/3 |     True    |             2              |
|    Vlan1    |    False    |             0              |
|   Vlan123   |    False    |             0              |
+-------------+-------------+----------------------------+
(py27_test)[mpenning@tsunami ~]$

You can use Inline::Python to embed ciscoconfparse in a perl script... but you still wind up writing in python, so I'm not really sure what the point is for a task this simple. 您可以使用Inline::Pythonciscoconfparse嵌入到perl脚本中...但是您仍然需要用python编写,因此我不太确定这个简单任务的意义是什么。


Disclaimer : I am CiscoConfParse 's author 免责声明 :我是CiscoConfParse的作者

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM