NSaid
NSaid

Reputation: 751

Bash - Command line argument being mutated in script

I have am having a bit of difficulty understanding weird behaviour of my script. First, here is the script in question:

processor_usage_script.sh

#!/bin/bash
#  This script will be used to obtain and store processor usage information for given ip

function join { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }

retrieve_processor_usage()
{
        # retrieve current id in host table for current IP address
        current_id=($(mysql -u root -phassan01 -D honours_project -e "SELECT id FROM hosts WHERE host_ip='$1'" -s -N))

        # get host os, as windows has different MIB OID for processor usage
        host_os=$(mysql -u root -phassan01 -D honours_project -e "SELECT host_os FROM host_descriptions WHERE host_id='$current_id'" -s -N)

        # take host os value and break it down to see if the os is windows based
        curr_host_os_temp=$(echo "$host_os" | sed 's/^.*\(Windows.*\)/\1/')
        curr_host_os=$(echo $curr_host_os_temp | tr -s ' ' | cut -d\  -f1)

        if [ "$curr_host_os" == "Windows" ]; then
                echo $( date '+%Y-%m-%d_%H-%M-%S' ) " - Host $1 is a $curr_host_os machine" >> logs/$( date '+%Y-%m-%d' )

                IFS=$'\n'
                var=($(snmpwalk -v1 -c public -Ovq -r 1 $1 HOST-RESOURCES-MIB::hrDeviceType))

                # Set up a counter value that I will use to get the values for the porcessor usage
                local count=1

                # create array to keep counts that will be used to get processor usage
                local count_array=()
                for i in "${var[@]}"; do
                        p=$(echo "$i" | sed 's/^.*\(hrDeviceProcessor\)/\1/')

                        if [[ "$p" == "hrDeviceProcessor" ]]; then
                                count_array=("${count_array[@]}" "$count")
                                count=$((count+1))
                        else
                                count=$((count+1))
                        fi
                done

                # declare processor usage array
                processor_usage=()

                # loop through count array and get processor usage
                for i in "${count_array[@]}"; do
                        p_usage=$(snmpget -v1 -c public -r 1 -Ovq $1 .1.3.6.1.2.1.25.3.3.1.2.$i)
                        #processor_usage=("${processor_usage[@]}" "$p_usage");
                        processor_usage+=("$p_usage");
                done

                # make processor usage comma separated
                processor_usage_comma_separated=$(join , "${processor_usage[@]}")
                #proces

sor_usage_comma_separated=$(printf "%s," "${processor_usage[@]}")

                    # pass values to log that values for processor usage has been inserted
                    echo $( date '+%Y-%m-%d_%H-%M-%S' ) " - Inserted into database processor usage details for $1 -- Processor Usage - $processor_usage_comma_separated" >> logs/$( date '+%Y-%m-%d' )
    echo "$processor_usage_comma_separated"
                    # insert value into database under host_processor_usage_windows
                    mysql -u root -phassan01 -D honours_project -e "INSERT INTO host_processor_usage_windows (id,host_id,system_time,processor_usage) VALUES ('','$current_id',NOW(),'$processor_usage_comma_separated')"
    echo $1
else
                # declare base ssCpuRaw variable
                ssCpuRaw=".1.3.6.1.4.1.2021.11"

                # use IP address to retrieve raw kernel usage (ssCpuRawKernel -- .1.3.6.1.4.1.2021.11.55.0)
                kernel_space_time=$(sudo snmpget -v1 -c public -Oqv -r 1 $1 $ssCpuRaw.55.0)

                # use IP address to retrieve raw user space usage (ssCpuRawUser -- .1.3.6.1.4.2021.11.50.0)
                user_space_time=$(sudo snmpget -v1 -c public -Oqv -r 1 $1 $ssCpuRaw.50.0)

                # use IP address to retrieve raw IO space usage, i.e. time spent waiting for IO (ssCpuRawUser -- .1.3.6.1.4.2021.11.54.0)
                io_space_time=$(sudo snmpget -v1 -c public -Oqv -r 1 $1 $ssCpuRaw.54.0)
ssor_usage=("${processor_usage[@]}" "$p_usage");
                        processor_usage+=("$p_usage");
                done

                # make processor usage comma separated
                processor_usage_comma_separated=$(join , "${processor_usage[@]}")
                #processor_usage_comma_separated=$(printf "%s," "${processor_usage[@]}")

                # pass values to log that values for processor usage has been inserted
                echo $( date '+%Y-%m-%d_%H-%M-%S' ) " - Inserted into database processor usage details for $1 -- Processor Usage - $processor_usage_comma_separated" >> logs/$( date '+%Y-%m-%d' )
echo "$processor_usage_comma_separated"
                # insert value into database under host_processor_usage_windows
                mysql -u root -phassan01 -D honours_project -e "INSERT INTO host_processor_usage_windows (id,host_id,system_time,processor_usage) VALUES ('','$current_id',NOW(),'$processor_usage_comma_separated')"
echo $1 retrieve current id in host table for current IP address
                current_id=($(mysql -u root -phassan01 -D honours_project -e "SELECT id FROM hosts WHERE host_ip='$1'" -s -N))

                if [ -n "$kernel_space_time" ] && [ -n "$user_space_time" ] && [ -n "$io_space_time" ]; then
                        echo $( date '+%Y-%m-%d_%H-%M-%S' ) " - Inserted into database processor usage details for $1 -- Kernel space: $kernel_space_time, User space: $user_space_time, IO space: $io_space_time" >> logs/$( date '+%Y-%m-%d' )

                        # insert values into database
                        mysql -u root -phassan01 -D honours_project -e "INSERT INTO host_processor_usage (id,host_id,system_time,kernel_space_time,user_space_time,io_space_time) VALUES ('','$current_id',NOW(),'$kernel_space_time','$user_space_time','$io_space_time')"
                fi

        fi

}

retrieve_processor_usage $1

What this script is suppose to do is take a given IP address and do an snmpget after retrieve the correct MIB id. The IP address is then used to do another retrieval using another script. When executed a lone this script works fine. The IP address passed in is the same at the end of execution of this script. However when run in the context it was designed for it fails. Here is the the context in which it executes:

main.sh

...
SECONDS=300

while true
do
for i in "${active_ip_open_161[@]}"
do
  . ./processor_usage_script.sh $i
  . ./memory_usage_script.sh $i
  . ./bandwidth_usage_script.sh $i
done

sleep $SECONDS
done
...

In the actual execution context a given IP is first passed into proceessor_usage_script.sh, then memory_usage_script.sh and so forth. In this case, after execution of processessor_usage_script.sh, a value of 7 gets returned and passed to the subsequent scripts. I don't understand why it is doing this, but I do believe I have narrowed it down to the execution of the MySQL statement. I was wondering if I could get help debugging why it is failing.

Upvotes: 2

Views: 335

Answers (1)

mklement0
mklement0

Reputation: 437176

Siguza's comment on the question hints at the problem:

By sourcing[1] the scripts you invoke (. <script> ...), these script's variables modify / add to the calling shell's variables.

Specifically, since $i is modified both in the calling script and inside processor_usage_script.sh, the next sourcing command, . ./memory_usage_script.sh $i, will pass whatever processor_usage_script.sh set $i to - not the original $i loop-variable value.

Solution options:

  • Use distinct variable names to avoid collisions.

  • Preferably, do not use sourcing - pass any information the helper scripts need via arguments, and use stdout output to pass information out.

    • Without sourcing, simply invoke your helper scripts with statements such as
      ./processor_usage_script.sh "$i" (no preceding .<space>)

Siguza also suggests double-quoting your variable references, which I've done above ("$i"), which is alway a good idea to ensure that the values aren't interpreted by the shell - even though in the case at hand (IP addresses) it happens to make no difference.


[1] Note that the linked POSIX spec. simply calls the . builtin utility dot, whereas Bash uses the term sourcing (and also allows use of source in lieu of .). Either way, the crucial aspect is that the specified script is loaded into the calling shell's context and therefore allows that script to modify the calling script's environment (variables, functions, aliases, shell option states, ...) - by contrast, direct invocation of a script causes it to run in a child process that has no impact on the calling shell's environment.

Upvotes: 4

Related Questions