rewalker3
rewalker3

Reputation: 41

Awk skip file if it doesn't exist

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

Answers (2)

muru
muru

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

Akshay Hegde
Akshay Hegde

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

Related Questions