I have 2 arrays in bash.
Array number 1 is the vlan subnet without the last octet.
Array number 2 is a list of octets i want to ignore, while scanning the subnet with Nmap.
lets assume every subnet has 254 pingable ip's (class c subnet)
I want the script to scan each subnet, and exclude ip's that ends with 1,2,3,252,253,254 which are Usually routers / firewalls / switches.
I manages to run 2 iterations, but failed on the if [[ $host == $host."${ignore[#]" ]] to identify the relevant ip (sunbet + ignore string)
Would really appreciate your help.
#!/bin/bash
# lets assume each subnet has 254 ips and all ignore ip's like 10.6.114.1 10.6.115.1 and 10.5.120.1
declare -a vlans=(
10.6.114
10.6.115
10.5.120
)
declare -a ignore=(
1
2
3
252
253
254
)
for vlan in "${vlans[#]}"; do
nmap -sn "$vlan" | grep Nmap | awk "{print $5}" | sed -n '1!p' | sed -e "$d" | sort > /tmp/vlan_ips.txt
readarray -t hosts < /tmp/vlan_ips.txt
for host in "${hosts[#]}"; do
check=$(echo "$host" | cut -d"." -f1-3)
if [ $host == $check."${ignore[#]}" ]; then
echo 'skipping record'
fi
done
done
This might work for you:
for vlan in "${vlans[#]}"; do
for ign in "${ignore[#]}"; do
printf '%s.%s\n' "$vlan" "$ign"
done >/tmp/ignore
nmap -n -sn "$vlan.0/24" -oG - 2>/dev/null |
grep -vwFf /tmp/ignore |
awk '/Host:/{print $2}' |
while read -r host; do
echo "$host"
done
done
Related
I am trying to create 2 arrays with bash.
array1 called DNSSERVERS : with all DNS Servers; output should be like this: 1.1.1.1,2.2.2.2,3.3.3.3,4.4.4.4
and
array2 called DNSSERVERSSEARCH.with all DNS Domain; output should be like this: local.domain.net,domain.net
all this information comes from: systemd-resolve --status.
Then I would like to put this array to a bash script called: bounding-netplan.sh
And the things is that today we have 4 DNS Server and 2 DNS domain.
but tomorrow it could be 1 DNS Server and 4 DNS Domain. The script must be flexible.
I tried to set with awk. but without success.
anyone can help me on this. will be very appreciated.
Thank you very much in advance.
# systemd-resolve --status
Global
DNS Servers: 1.1.1.1
2.2.2.2
3.3.3.3
4.4.4.4
DNS Domain: local.domain.net
domain.net
DNSSEC NTA: 10.in-addr.arpa
16.172.in-addr.arpa
168.192.in-addr.arpa
17.172.in-addr.arpa
18.172.in-addr.arpa
19.172.in-addr.arpa
20.172.in-addr.arpa
21.172.in-addr.arpa
22.172.in-addr.arpa
23.172.in-addr.arpa
24.172.in-addr.arpa
25.172.in-addr.arpa
26.172.in-addr.arpa
27.172.in-addr.arpa
28.172.in-addr.arpa
29.172.in-addr.arpa
30.172.in-addr.arpa
31.172.in-addr.arpa
corp
home
internal
intranet
lan
local
private
test
cat bounding-netplan.sh
#!/bin/bash
MAJORRELEASE=$( lsb_release -sr | cut -d\. -f1 )
STROS=$( lsb_release -si )
# Ubuntu 18.04
if [ $STROS == Ubuntu ] && [ $MAJORRELEASE -ge 18 ] ; then
if [ -d /etc/netplan ]; then
DNSSERVERS=``
DNSSERVERSSEARCH=``
cat <<EOF | tee /etc/netplan/01-netcfg.yaml
network:
version: 2
renderer: networkd
ethernets:
switchports:
match: {name: "enp*"}
bonds:
bond0:
interfaces: [switchports]
addresses: [${IP}]
gateway4: ${ROUTE}
nameservers:
search: [${DNSSERVERSSEARCH}]
addresses: [${DNSSERVERS}]
EOF
fi
fi
I think found a way here:
$ systemd-resolve --status | sed -e 's#[\t ]##g' | awk -F\: 'BEGIN{section=""}{if($2!=""){section=$1; print $1" "$2}else {print section" "$1}}' | awk '{if($1=="DNSServers") print $2}' | sort -u | tr '\n' ',' | sed -e 's#,$##'
1.1.1.1,2.2.2.2,3.3.3.3,4.4.4.4
The former only work when it's only IPv4, this work for both IPv4 and IPv6:
$ systemd-resolve --status | sed -e 's/: /=/' | sed -e 's#[\t ]##g' | awk -F= 'BEGIN{section=""}{if($2!=""){section=$1; print $1" "$2}else {print section" "$1}}' | awk '{if($1=="DNSServers") print $2}' | sort -u | tr '\n' ',' | sed -e 's#,$##'
8.8.4.4,8.8.8.8,fd80::7a98:e8ff:fe46:4328,fe80::1
I have this arrays, with output to a file.txt:
the first array contains all IPs in subnet (192.168.1.0, 192.168.1.1, ecc)
the second array contains an 1 if that host is up, a 0 if down, in order to obtain a matrix like this:
192.168.1.0 192.168.1.1 ...
0 1 ...
How can I allign this elements? I want that the bit status (0 or 1) is at the center of the IP address.
Consider that the IP address may vary, depending on the machine, for example it can be
71.5.0.0
I put all my script if you need more info:
#!/bin/bash
# written by Cosimo Colaci
# Declarations
ROOT_UID=0
E_NOROOT=65
declare -a iplist
echo "Reading hosts status..."
# Must run as root.
# if [ "$UID" -ne "$ROOT_UID" ]
# then
# echo "You need to run this script as root. Exiting..."
# exit $E_NOROOT
# fi
# Find Gateway IP and Netmask
ip_gateway=$(route | tail -1 | awk '{print $1}')
netmask=$(route | tail -1 | awk '{print $3}')
# echo "$netmask" FOR DEBUG
# Find subnet
OLDIFS="$IFS"
IFS=.
read -r i1 i2 i3 i4 <<< "$ip_gateway"
read -r m1 m2 m3 m4 <<< "$netmask"
subnet_ip=$(printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((ii4 & m4))")
# echo "$subnet_ip" FOR DEBUG
IFS="$OLDIFS"
# Express netmask in CIDR notation, to know how many hosts to scan
cidr_by_netmask() {
#function returns prefix for given netmask in arg1
bits=0
for octet in $(echo $1| sed 's/\./ /g'); do
binbits=$(echo "obase=2; ibase=10; ${octet}"| bc | sed 's/0//g')
let bits+=${#binbits}
done
echo "/${bits}"
}
suffix=$(cidr_by_netmask $netmask)
# echo "$suffix" FOR DEBUG
# First raw of matrix file: all IPs in subnet
iplist=( $(nmap -sL $subnet_ip$suffix | grep "Nmap scan report" | awk '{print $NF}') )
# echo "${iplist[#]}" FOR DEBUG
let i=1
let j=0
while [ $j -lt ${#iplist[#]} ]
do
raw1[$i]=${iplist[$j]}
((i++))
((j++))
done
raw1[0]="#IP/TIME"
for value in ${raw1[#]}
do
echo -en "$value\t"
done >ip-time_matrix.txt
echo -e "\n" >>ip-time_matrix.txt
# Other raws
let j=1
let k=0
export j
export k
echo "Scanning hosts every 10s..."
echo "Hit CTRL-C to stop and check file \"ip-time_matrix.txt\""
while true
do
nmap -sP $subnet_ip$suffix &>temp_esame95a.txt
raw2[0]="$(date +%T)"
for ip in ${iplist[#]}
do
if grep "$ip" temp_esame95a.txt &>/dev/null
then
raw2[$j]="---1---"
else
raw2[$j]="---0---"
fi
((j++))
done
for value in ${raw2[#]}
do
echo -en "$value\t"
done >>ip-time_matrix.txt
echo -e "\n" >>ip-time_matrix.txt
sleep 10
j=1
done
In a comment a suggested printf "%15.15s %s" "${ip}" ${bitstatus}. I should have added a \n, printf "%15.15s %s\n" "${ip}" ${bitstatus}.
OP reacted, that it solved his problem.
For the arrays:
ips=(192.168.1.0 127.001.0.1 192.168.1.1 192.169.1.1)
raw=(1 1 0 1)
you could use printf and cut commands,
printf "%15s" "${ips[#]}" | cut -c5-
printf "%15s" "${raw[#]}" | cut -c5-
to align the output:
192.168.1.0 127.001.0.1 192.168.1.1 192.169.1.1
1 1 0 1
I look for a way to count the login attempts from each ip by parsing a log file (var/log/auth). I already found a way to do this but it is ugly and needs a lot of time because of the fu*** file operations.
Couldnt this be done with variables only? What I want is a listing like ip=count...
Many thanks
This is what I dont want :)
for ip in $(cat /var/log/auth.log | grep sshd | grep Failed | grep -v invalid | awk '{ print $11; }'); do
if [ -e "log/$ip" ]; then
file="log/$ip"
counter=$(cat "$file")
counter=$[$counter +1]
echo $counter > log/$ip
else
echo 1 >> log/$ip
fi
done
A sample from the logfile is
Jul 30 21:07:30 Server sshd[20895]: Failed password for root from 58.242.83.20 port 41448 ssh2
Jul 30 21:07:31 Server sshd[20897]: Failed password for root from 61.177.172.44 port 28603 ssh2
What I want is something like
58.242.83.20=1932
61.177.172.44=3
grep -Po 'sshd.*Failed password for [^i]* \K[0-9.]{7,14}' file | sort | uniq -c
works perfect for me...
thanks to Cyrus
try one more approach with awk, where it will provide you output in same order in which IPs have come into your Input_file.
awk '!/invalid/ && /Server sshd.*Failed password/{
match($0,/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/);
val=substr($0,RSTART,RLENGTH);
if(!a[val]){
b[++i]=val
};
a[val]++
}
END{
for(j=1;j<=i;j++){
print b[j],a[b[j]]
}
}' Input_file
This is an idea using awk with sort and uniq:
awk '$5 ~ "sshd" && $6 = "Failed" && $9 != "invalid" { print $11 }' auth.log | sort | uniq -c
Or just awk:
awk '$5 ~ "sshd" && $6 = "Failed" && $9 != "invalid" { ips[$11]++ } END { for (ip in ips) print ip"="ips[ip]}' auth.log
I'm trying to understand what I'm doing wrong here, but can't seem to determine the cause. I would like to create a set of arrays from an output for a for loop in bash. Below is the code I have so far:
for i in `onedatastore list | grep pure02 | awk '{print $1}'`;
do
arr${i}=($(onedatastore show ${i} | sed 's/[A-Z]://' | cut -f2 -d\:)) ;
echo "Output of arr${i}: ${arr${i}[#]}" ;
done
The output for the condition is as such:
107
108
109
What I want to do is based on these unique IDs is create arrays:
arr107
arr108
arr109
The arrays will have data like such in each:
[oneadmin#opennebula/]$ arr107=($(onedatastore show 107 | sed 's/[A-Z]://' | cut -f2 -d\:))
[oneadmin#opennebula/]$ echo ${arr107[#]}
DATASTORE 107 INFORMATION 107 pure02_vm_datastore_1 oneadmin oneadmin 0 IMAGE vcenter vcenter /var/lib/one//datastores/107 FILE READY DATASTORE CAPACITY 60T 21.9T 38.1T - PERMISSIONS um- u-- --- DATASTORE TEMPLATE CLONE_TARGET="NONE" DISK_TYPE="FILE" DS_MAD="vcenter" LN_TARGET="NONE" RESTRICTED_DIRS="/" SAFE_DIRS="/var/tmp" TM_MAD="vcenter" VCENTER_CLUSTER="CLUSTER01" IMAGES
When I try this in the script section though I get output errors as such:
./test.sh: line 6: syntax error near unexpected token `$(onedatastore show ${i} | sed 's/[A-Z]://' | cut -f2 -d\:)'
I can't seem to figure out the syntax to use on this scenario.
In the end what I want to do is be able to compare different datastores and based on which on has more free space, deploy VMs to it.
Hope someone can help. Thanks
You can use the eval (potentially unsafe) and declare (safer) commands:
for i in $(onedatastore list | grep pure02 | awk '{print $1}');
do
declare "arr$i=($(onedatastore show ${i} | sed 's/[A-Z]://' | cut -f2 -d\:))"
eval echo 'Output of arr$i: ${arr'"$i"'[#]}'
done
readarray or mapfile, added in bash 4.0, will read directly into an array:
while IFS= read -r i <&3; do
readarray -t "arr$i" < <(onedatastore show "$i" | sed 's/[A-Z]://' | cut -f2 -d:)
done 3< <(onedatastore list | awk '/pure02/ {print $1}')
Better, back through bash 3.x, one can use read -a to read to an array:
shopt -s pipefail # cause pipelines to fail if any element does
while IFS= read -r i <&3; do
IFS=$'\n' read -r -d '' -a "arr$i" \
< <(onedatastore show "$i" | sed 's/[A-Z]://' | cut -f2 -d: && printf '\0')
done 3< <(onedatastore list | awk '/pure02/ {print $1}')
Alternately, one can use namevars to create an alias for an array with an arbitrarily-named array in bash 4.3:
while IFS= read -r i <&3; do
declare -a "arr$i"
declare -n arr="arr$i"
# this is buggy: expands globs, string-splits on all characters in IFS, etc
# ...but, well, it's what the OP is asking for...
arr=( $(onedatastore show "$i" | sed 's/[A-Z]://' | cut -f2 -d:) )
done 3< <(onedatastore list | awk '/pure02/ {print $1}')
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. 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. What I am looking for is a way to read the array for three things:
interface name
switchport access vlan
shutdown
The basic flow is that the script (interfaces.sh) reads in any *.cfg file and reads the interfaces in as an array. 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.
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 #
# 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. I use an IOS configuration file parser (ciscoconfparse) library to avoid bugs in my own parsing attempt. I've given it a command line interface so to see the state of interfaces on vlan 2:
$ python interfaces.py --vlan 2 /path/to/ios.cfg
Installation
Install Python 2.7
Install cisoconfparse
Save the script below as some .py (e.g., 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.
#!/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. The ${interfaces[#]} array contains the each interface definition as a separate element that can be string-searched for other data.
You shouldn't try to parse hierarchical text files (like a Cisco IOS configuration) with bash scripts... use a canned configuration parsing library...
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)...
The code for parsing out interface name, switchport status and access vlan number is this simple...
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.
Disclaimer: I am CiscoConfParse's author