Parsing cisco cfg to export as csv

I have been looking everywhere for a solution and it seems as though awk may do the trick. I am very much a newbie in the awk scripting world but it seems to appear one of the best text parsing tools from what I've read on this forum.

Scenario:
I have about 50 cisco config files randomly named. I need to create a script that looks in each config file (*.cfg) located on the local drive in the same directory as the script for

interface Vlan227 <-- need one column to be the data after interface as long as it begins with interface Vlan
ip address 10.47.203.2/25 <-- need another column to be whatever is after ip address (if it even exists)

description <-- need this in its entirity (if it exists)

ip dhcp relay address 10.47.144.76 <-- need the address (could be more than one line up to 6)
or
ip helper-address 10.47.144.76 <-- need the address (could be more than one line up to 6)

I attached an example of one of the config files

My thought on approach would possibly be using a bash script calling awk

#!/bin/bash
VALUE="interface Vlan"
awk /"$VALUE"/' { print previousline; }; { previousline=$0; }' file.cfg

Any help figuring this out would be very educational and very helpful if you can add some comments on what the code is doing so I can wrap my old brain around it.

what would be the expected output of this block?

interface Vlan229
  no ip redirects
  ip address 10.47.51.2/24
  no ipv6 redirects
  ip router ospf 54775 area 0.0.0.44
  hsrp 229
    preempt
    priority 110
    ip 10.47.51.1
  ip dhcp relay address 10.47.144.131
  ip dhcp relay address 10.47.144.132
  ip dhcp relay address 10.47.144.131
  ip dhcp relay address 10.47.144.132
  description Dev-App_NAOTEST_XEN_DT_LAN_VLAN1
  no shutdown

---------- Post updated at 01:46 PM ---------- Previous update was at 01:19 PM ----------

something to start with.
awk -f dj.awk myCiscoFile.cfg where dj.awk is:

BEGIN {
  OFS=","
  qq="\""
}
FNR==1 {print "Interface", "IP address", "Description", "IP dhcp", "IP helper"}
/^interface/ { f=1; vlan=$2;next}
f && NF {
  if (/ip address/) ip=(ip)?ip OFS $NF: qq $NF
  if (/description/) desc=(desc)?desc OFS $0: qq $0
  if (/dhcp relay/) dhcp=(dhcp)?dhcp OFS $NF: qq $NF
  if (/helper-address/) help=(help)?help OFS $NF: qq $NF
  next
}
{ print vlan, (ip)? ip qq:"", (desc)?desc qq:"", (dhcp)?dhcp qq:"", (help)?help qq:""
  f=0; ip=desc=dhcp=help=""
}
1 Like

Thats incredible. Exactly what I was trying to do. Your script makes it look so easy. I really appreciate it. Thank You. Now I would like to try and dissect what you are doing so I can learn how its working. :slight_smile:

I'm trying to figure out how you did your magic for learning purposes and entering what I believe is happening with comments.

I'm a tad confused on the variable settings after you find the matching string (example /description/)

#! /bin/awk -f
# awk -f findit.awk /home/shared/Configs/.config > output.csv
BEGIN {
  OFS=","
  qq="\""
}

# FNR is the current record in the current file and increments each time a new record is read and starts at 0 each time a new input file is started
FNR==1 {print "Interface", "IP address", "Description", "IP dhcp", "IP helper"}

# find any line that begins with interface and enter
/^interface/ { f=1; vlan=$2;next}
f && NF {

# If you find the string ip address then create variable ip and add
  if (/ip address/) ip=(ip)?ip OFS $NF: qq $NF
  if (/description/) desc=(desc)?desc OFS $0: qq $0
  if (/dhcp relay/) dhcp=(dhcp)?dhcp OFS $NF: qq $NF
  if (/helper-address/) help=(help)?help OFS $NF: qq $NF
  next
}

# Print variables vlan, (ip)?
{ print vlan, (ip)? ip qq:"", (desc)?desc qq:"", (dhcp)?dhcp qq:"", (help)?help qq:""
  f=0; ip=desc=dhcp=help=""
}

For the description line, you shouldn't use $0 as is, but first remove $1 (which happens to be the string "description") from $0 like sub (" *" $1 " ", "", $0) .

#! /bin/awk -f
# awk -f findit.awk /home/shared/Configs/.config > output.csv
BEGIN {
  OFS=","
  qq="\""
}

# FNR is the current record in the current file and increments each time a new record is read and starts at 0 each time a new input file is started
# edit: you probably need NR (instead of FNR) if you have multiple config files to parse
#        this prints out the header line in the resulting csv
NR==1 {print "Interface", "IP address", "Description", "IP dhcp", "IP helper"}

# find any line that begins with interface and enter
# edit: 'interface' line is the indication of the start of a new block => set flag f to 1
#         as the indication of a new block. Assign vlan a value from the second field
#         skip the rest of the code and go to the 'next' record/line
/^interface/ { f=1; vlan=$2;next}

# edit: if we're in the block section (f is not 0) AND the line/record is not empty 
#        (NF!=0 or NF), enter the block lines prasing...
f && NF {

# If you find the string ip address then create variable ip and add
# edit: if 'ip address' is present on a current line... if 'ip' valiable is NOT empty, add to it
#        the value of the last (NF) field prefixed with the OFS (,). If it's empty, prefix the
#        last field with the duoble quote - beginning of the OFS-separated list
#        Same logic for the other lines to be identified: description dhcp etc..
  if (/ip address/) ip=(ip)?ip OFS $NF: qq $NF

  if (/description/) desc=(desc)?desc OFS $0: qq $0
  if (/dhcp relay/) dhcp=(dhcp)?dhcp OFS $NF: qq $NF
  if (/helper-address/) help=(help)?help OFS $NF: qq $NF
  next
}

# edit: got to this block if we're outside the 'block' - empty line (!NF) - see above
# Print variables vlan, (ip)?
# edit: print the accumulated variable from parsing the previous 'block - see above
#         (ip)? ip qq:"" => means:if 'ip' is not empty, print 'ip' followed by a double quote
#                                         (end of the OFS-separated list; 
#                                         if 'ip' is empty, output empty string ("")
#        same for the other vars
{ print vlan, (ip)? ip qq:"", (desc)?desc qq:"", (dhcp)?dhcp qq:"", (help)?help qq:""
  f=0; ip=desc=dhcp=help=""
}
1 Like

Is there a possible limitation on lines? Trying on another config and getting blank lines:

,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,
,,,,

So I did a

$ cat Configs/blahblah.Config | grep ^interface

It shows 159 lines since it also shows other interfaces besides just interface Vlan
example of results:

interface GigabitEthernet5/39
interface GigabitEthernet5/40
interface GigabitEthernet5/41
interface GigabitEthernet5/42
interface GigabitEthernet5/43
interface GigabitEthernet5/44
interface GigabitEthernet5/45
interface GigabitEthernet5/46
interface GigabitEthernet5/47
interface GigabitEthernet5/48
interface Vlan1
interface Vlan50
interface Vlan101
interface Vlan200
interface Vlan400

So to troubleshoot and trying to figure this out on my own in hopes to learn I started at print statements to validate the variables are being populated so I altered the code to look like this and the output is ugly but you can see there is stuff in there:

#! /usr/bin/awk -f
# awk -f findnet.awk Configs/.config > output.csv
BEGIN {
  OFS=","
  qq="\""
}

# FNR is the current record in the current file and increments each time a new record is read and starts at 0 each time a new input file is started
# edit: you probably need NR (instead of FNR) if you have multiple config files to parse
#        this prints out the header line in the resulting csv
NR==1 {print "Interface", "IP address", "Description", "IP dhcp", "IP helper"}

# find any line that begins with interface and enter
# edit: 'interface' line is the indication of the start of a new block => set flag f to 1
#         as the indication of a new block. Assign vlan a value from the second field
#         skip the rest of the code and go to the 'next' record/line
/^interface/ { f=1; vlan=$2;next}
{ print vlan }

# edit: if we're in the block section (f is not 0) AND the line/record is not empty
#        (NF!=0 or NF), enter the block lines prasing...
f && NF {

# If you find the string ip address then create variable ip and add
# edit: if 'ip address' is present on a current line... if 'ip' valiable is NOT empty, add to it
#        the value of the last (NF) field prefixed with the OFS (,). If it's empty, prefix the
#        last field with the duoble quote - beginning of the OFS-separated list
#        Same logic for the other lines to be identified: description dhcp etc..
  if (/ip address/) ip=(ip)?ip OFS $NF: qq $NF
print ip
  if (/description/) desc=(desc)?desc OFS $0: qq $0
print desc
  if (/dhcp relay/) dhcp=(dhcp)?dhcp OFS $NF: qq $NF
print dhcp
  if (/helper-address/) help=(help)?help OFS $NF: qq $NF
print help
  next
}

# got to this block if we're outside the 'block' - empty line (!NF) - see above
# print the accumulated variable from parsing the previous 'block - see above
#         (ip)? ip qq:"" => means:if 'ip' is not empty, print 'ip' followed by a double quote
#                                         (end of the OFS-separated list;
#                                         if 'ip' is empty, output empty string ("")
#        same for the other vars
{ print vlan, (ip)? ip qq:"", (desc)?desc qq:"", (dhcp)?dhcp qq:"", (help)?help qq:""
  f=0; ip=desc=dhcp=help=""
}

Am i approaching this correctly?

if you post the config(s) that break the code, it might be easier to troubleshoot.
Attaching a sample config file(s) could be good.