Bash 101 Part 3: Platform Detection and Dynamic (Run-time) command execution


Bash gives you the ability to create a generic script which can execute a run-time specified command with run time specified arguments (variable based execution). This is an effective trick which has proven useful in our QA Automation system.

This article aims to:

  • Demonstrate platform detection and...
  • Show how to execute a command that Differs from platform to platform

Notes:

 

Platform Detection

Here is a sample script which can be used to define which executable to run based off of Host OS. It also sets a few related paths for Testcases:

#!/bin/bash
#########Define Platform Constants############
HOSTNAME= hostname > /dev/null
UNAME= uname -a > /dev/null
PLATFORM="null"
PATH_TO_SQ3_DIR="null"
PATH_TO_EXE="null"

PATH_TO_TESTCASE="null"
PATH_TO_TESTCASE_FILES="null"

# Determine Platform (Win/NIX) constants
if [ -d /cygdrive ]
then
  if [ -d /cygdrive/c/Program\ Files\ \(x86\) ]
  then
    PLATFORM="win64"
    PATH_TO_SQ3_DIR=/cygdrive/c/Program\ Files/Vendor_Path/Files/DBs
    PATH_TO_EXE=/cygdrive/c/Program\ Files/Vendor_Path/Platform/Vendor_executable.exe
    PATH_TO_TESTCASE=~/QAAutomation/TestCases/TestCase1
    PATH_TO_TESTCASE_FILES=~/QAAutomation/TestCases/TestCase1/Files
  else
    PLATFORM="win32"
    PATH_TO_SQ3_DIR=/cygdrive/c/Program\ Files/Vendor_Path/Files/DBs
    PATH_TO_EXE=/cygdrive/c/Program\ Files/Vendor_Path/Platform/Vendor_executable.exe
    PATH_TO_TESTCASE=~/QAAutomation/TestCases/TestCase1
    PATH_TO_TESTCASE_FILES=~/QAAutomation/TestCases/TestCase1/Files
  fi
else
  if [[ $UNAME == *ia64* ]]
  then
      PLATFORM="hpia"
  else
      if [[ $UNAME == *HP-UX* ]]
      then
        PLATFORM="hpppa"
      fi
  fi
 
  if [[ $UNAME == *sparc* ]]
  then
      PLATFORM="sunsparc"
  fi

  if [[ $UNAME == *AIX* ]]
  then
      PLATFORM="aixppc"
  fi
 
  if [[ $UNAME == *x86_64* ]]
  then
      PLATFORM="linux64"
  else
      PLATFORM="linux32"
  fi
 
  PATH_TO_SQ3_DIR=/var/opt/Vendor_Path/Files/DBs
  PATH_TO_EXE=/opt/Vendor_Path/agent/bin/vendor_exe_for_nix
  PATH_TO_TESTCASE=~/QAAutomation/TestCases/TestCase1
  PATH_TO_TESTCASE_FILES=~/QAAutomation/TestCases/TestCase1/Files
fi
##############################################

 

Here's what's going on:

The first block at the top sets the initial values for variables. Not required, but I've placed it at the top for readability/comprehension:

HOSTNAME= hostname > /dev/null
UNAME= uname -a > /dev/null
PLATFORM="null"
PATH_TO_SQ3_DIR="null"
PATH_TO_EXE="null"

PATH_TO_TESTCASE="null"
PATH_TO_TESTCASE_FILES="null"

HOSTNAME is populated by the hostname command. Somewhat counter-intuitively, I am piping the StandardOut to the bit bucket (/dev/null). In our automation scripts the excess command output displayed by the command can cause the script to fail as it needs to return a specific value. This allows the variable to be set and out test to proceed.

The UNAME variable is populated in a similar way to HOSTNAME, except that I'm using a different command. uname (-a) will return information about the host OS. This is used further down to determine which platform we are running on.

 

The next block has a lot of if then else statements to determine the host OS and processor architecture. I'll go over the conditionals to explain the logic:

First, I determine if we are running on Windows. If so, I check for 32/64 bit versions:

if [ -d /cygdrive ]
then
  if [ -d /cygdrive/c/Program\ Files\ \(x86\) ]
  then
    PLATFORM="win64"
  else
    PLATFORM="win32"
  fi

This is done by checking for /cygdrive. If it exists, I know I'm running in Cygwin on Windows.

The next check is made to see if there is a C:\Program Files (x86) directory on the machine. If so, I know I'm running a 64-bit variant of windows.

If the script is NOT executing on a windows machine, I run through some *NIX Platform detection:

else
  if [[ $UNAME == *ia64* ]]
  then
      PLATFORM="hpia"
  else
      if [[ $UNAME == *HP-UX* ]]
      then
        PLATFORM="hpppa"
      fi
  fi
 
  if [[ $UNAME == *sparc* ]]
  then
      PLATFORM="sunsparc"
  fi

  if [[ $UNAME == *AIX* ]]
  then
      PLATFORM="aixppc"
  fi
 
  if [[ $UNAME == *x86_64* ]]
  then
      PLATFORM="linux64"
  else
      PLATFORM="linux32"
  fi
fi

Each if Statement checks to see which OS / processor architecture is in use. To check for HP-UX, I first check for ia64 (Itanium). If that doesn't match, but HP-UX is returned by uname, then I know I'm running on a PPA box.

If it's not an HP-UX box, then I check for Sparc (Solaris), AIX and Linux. A few things to note:

  • The double bracket ( [[ ) is used instead of single in these instances to unlock additional functaionlity. Double bracket is a bash operator versus the single bracket which is an alias for the test command.
  • Asterisks are used in the conditionals. The double-bracket lets us use this syntax to see if a return value contain the listed platform designator
  • We've cut it out here for breveity, but we escape spaces when defining paths without double-quoting. This seemed to work for u. You might want to experiment here.

 

Executing Dynamic Commands and capturing the results

Once we know the platform we are working and and have set the value/location of the executable, we can proceed with the rest of the script logic. Here is a stripped-down example from one of our test scripts:

######### Test Case Execution Steps ##########
EXECUTABLE="\""$PATH_TO_EXE"\""
SET_COMMAND=-c\ address
VALIDATE_COMMAND=-l\ address
VALUE="10.190.3.65"

# Set EXE Configuration (CLI)

RESULT_SET=$(eval $EXECUTABLE $SET_COMMAND$VALUE )

# Check EXE Configuration (CLI)
RESULT_VALIDATE=$(eval $EXECUTABLE $VALIDATE_COMMAND )

if [[ "$RESULT_VALIDATE" == "$VALUE" ]]
then
  echo "test passed"
else
  echo "test failed"
fi


In this example, we set the variable EXECUTABLE to equal the double-quoted value of the Fully qualified path to the Executable (Defined in the previous section). This command is executed and the results are validated. There are a few things to note:

  • We double-quote the path to the executable and store it in another variable so the eval statement works correctly.
  • The $(eval COMMAND ARGS) syntax allows us to execute whichever executable is applicable to the Host OS. eval will evaluate the variable and return the result, where $() allows for the result to be assigned to a result variable for conditional processing. We found that we were not able to capture command output without nesting eval in $()
  • In the conditional to check the command line output, the variables are double-quoted so they are interpreted as literal strings. This helps make the comparison more reliable for our use-cases.

 

So, Here are the boiled-down steps for dynamic / run-time specified commands and capturing their output:

  1. Define a variable with a the path to the executable. This can be passed in as a bash argument, or calculated in-line in the script (as we did above)
  2. Define a variable which takes the PATH_TO_EXE variable defined in step 1 and adds quotes around the path. I found this to be required in my testing. Without the double quotes, my command execution would fail with spaces in the exe path.
  3. Define a RESULTS variable which is set equal to: $(eval $EXE $ARGSS)
    Note: you don't have to put arguments in a variable. They can be in-line like this: $(eval $EXE -la)

That should be it. If you have anything to add, let me know (See the about page for contact information). My comment system doesn't email me automatically when a comment is made