Reputation: 41
I've spent some time trying to figure this out with various internet searches and digging through stackoverflow. I'll try to explain this as best as a noob can.
I have a script that searches a config repository directory that is populated with directories for every Juniper and Cisco router and switch we have deployed. Each device directory has a file or two that I'm interested in, "show.version" and "show.chassis.hardware", except when they don't. The second file, "show.chassis.hardware" is not a command that Cisco boxes has, so that file does not exist in Cisco device directories. There also isn't a naming scheme that can easily tell me if the device is Juniper or Cisco, which is also part of the reason why my script exists.
To make things more fun, different models and even software versions output the show.version file in different formats (for both Cisco and Juniper), so my awk is full of all the different fields we will encounter.
Script:
#!/usr/local/bin/zsh
svn="$HOME/svn/nw_config_data/"
case "$1" in
("-a")
hosts=""
;;
("-b")
hosts=".bb.domain.net"
;;
("-c")
hosts=".cpe.domain.net"
;;
("-e")
hosts=".etech.domain.net"
;;
("-k")
hosts=".core.domain.net"
;;
("-m")
hosts=".maint.domain.net"
;;
("-o")
hosts=".ohgov.domain.net"
;;
esac
dirs=($(ls -d $svn*$hosts*))
for hostdir in $dirs
do host=$(echo $hostdir | grep -Eo "(\w|\.|-)*$")
awk -v h=$host '/^Model/{m=$2} /^Model number/{m=$4} /^\*0/{m=$2}
/^JUNOS Base OS boot/{v=$5} /^Junos:/{v="["$2"]"} /^BOOTLDR:/{v=$7}
/^JUNOS EX Software Suite/{v=$5} /^ROM:/{v=$5} /^JUNOS Software
Release/{v=$4} /^Chassis/{s=$2} /^Motherboard serial number/{s=$3}
END {if(m!="number") {printf("%s %s %s %s\n",h,m,v,s)}}'
"$hostdir/show.version" "$hostdir/show.chassis.hardware"
done
What it looks like when I run the script:
% cver -b
device1-e0.bb.domain.net ex4300-24t [14.1X53-D25.2] Serial#
device2-e0.bb.domain.net ex4300-24t [14.1X53-D25.2] Serial#
awk: can't open file /home/clmbn eng2/a/rwalker/svn/nw_config_data/device3-e1.bb.domain.net/show.chassis.hardware
input record number 55, file /home/clmbn-eng2/a/rwalker/svn/nw_config_data/device3-e1.bb.domain.net/show.chassis.hardware
source line number 1
device4-r0.bb.domain.net m7i [13.3R6.5] Serial#
...
What I want it to look like
% cver -b
device1-e0.bb.domain.net ex4300-24t [14.1X53-D25.2] Serial#
device2-e0.bb.domain.net ex4300-24t [14.1X53-D25.2] Serial#
device3-e1.bb.domain.net C3750 12.1(14r)EA1a, Serial#
device4-r0.bb.domain.net m7i [13.3R6.5] Serial#
...
I have 13 directories that do not have the "show.chassis.hardware" file but do have the "show.version" file which does have all the information I need from it. I have one directory that has no files, but it doesn't matter because that device is going to be replaced.
From what I've been reading, awk might not be able to do this, but I have faith that someone out there knows a way to make it work. If my approach (shell & awk scripting) just isn't going to work and I need to do it in something else (Perl or Python for example) I'll be completely stuck until I can learn those enough to convert my script to one of those languages.
Also, we don't have bash installed on this server and I don't know when we will since I'm not the admin.
Upvotes: 1
Views: 1466
Reputation: 4887
You can use zsh's (...|...)
globbing:
$ mkdir foo bar; touch {foo,bar}/show.version foo/show.chassis.hardware
$ echo foo/show.(version|chassis.hardware)
foo/show.chassis.hardware foo/show.version
$ echo bar/show.(version|chassis.hardware)
bar/show.version
Since this is globbing, it will only expand to existing files. So your awk command would look like:
awk -v h=$host '/^Model/{m=$2} ... END {...}' "$hostdir"/show.(version|chassis.hardware)
(omitting the awk script for readability)
I'd also simplify your script a bit using associative arrays instead of cases:
#!/usr/local/bin/zsh
usage () {
echo "Help!" # echo your help message here
exit $1
}
svn="$HOME/svn/nw_config_data/"
declare -A hosts # make associative array
hosts["-a"]=""
hosts["-b"]=".bb.domain.net"
hosts["-c"]=".cpe.domain.net"
hosts["-e"]=".etech.domain.net"
hosts["-k"]=".core.domain.net"
hosts["-m"]=".maint.domain.net"
hosts["-o"]=".ohgov.domain.net"
if [[ $1 == -h ]]
then
usage
elif (( ${+hosts["$1"]} )) # check if $1 is a key in hosts
then
echo "Invalid option: $1"
usage 1 # exit with status 1 to indicate error
fi
dirs=( $svn*$hosts["$1"]* ) # no need for ls here
for hostdir in $dirs
do
host=$(echo $hostdir | grep -Eo "(\w|\.|-)*$")
awk -v h=$host '
/^Model/{m=$2}
/^Model number/{m=$4}
/^\*0/{m=$2}
/^JUNOS Base OS boot/{v=$5}
/^Junos:/{v="["$2"]"}
/^BOOTLDR:/{v=$7}
/^JUNOS EX Software Suite/{v=$5}
/^ROM:/{v=$5}
/^JUNOS Software Release/{v=$4}
/^Chassis/{s=$2}
/^Motherboard serial number/{s=$3}
END {if(m!="number") {printf("%s %s %s %s\n",h,m,v,s)}}' \
"$hostdir"/show.(version|chassis.hardware)
done
Alternately, you can use a concise case
with the array:
declare -A hosts # make associative array
hosts["-a"]=""
hosts["-b"]=".bb.domain.net"
hosts["-c"]=".cpe.domain.net"
hosts["-e"]=".etech.domain.net"
hosts["-k"]=".core.domain.net"
hosts["-m"]=".maint.domain.net"
hosts["-o"]=".ohgov.domain.net"
case $1 in
-[abcekmno]) dirs=( $svn*${hosts["$1"]}* )
;;
-h|help) usage
;;
*) echo "Invalid option: $1"
usage 1 # exit with status 1 to indicate error
;;
esac
Upvotes: 1
Reputation: 16997
You need
-f file
true if file exists and is a regular file.
if [[ -f "$hostdir/show.version" && -f "$hostdir/show.chassis.hardware" ]]; then
# your awk command goes here...
awk '{ }' "$hostdir/show.version" "$hostdir/show.chassis.hardware"
else
echo "not enough files found"
fi
You may refer : http://zsh.sourceforge.net/Doc/Release/Conditional-Expressions.html
--edit--
That's cool that this bit of script skips the directories that doesn't have the file, but it doesn't pull the information from the "show.version" file and print that information anyway. So the output shows device1, device2, device 4...
Here is code snippet
function myfunc(){
# replace with your awk
awk '{ print }' "$@"
}
if [[ -f "$hostdir/show.version" && -f "$hostdir/show.chassis.hardware" ]]; then
# your awk command goes here...
myfunc "$hostdir/show.version" "$hostdir/show.chassis.hardware"
else
echo "not enough files found"
# pass only one file, version file
myfunc "$hostdir/show.version"
fi
Upvotes: 2