DX Application Performance Management

Shell scripting: CLI UI handling

By Jörg Mertin posted 03-25-2020 11:01 AM

  

Shell scripting: CLI UI handling

UI handling is usually required when guiding a user through a certain process.
If every developer goes line by line, every developer will have his own "design" of UI, and his own style. Some are good, some are bad, and when having lots of questions to ask, change etc., it can become confusing very fast.
Using a set of functions to actually separate the logical steps can come in handy, especially when on the CLI.

The functions

The functions to use can be very simple. The main reason to use functions is that in case the layout is to change, one can change the content of only the required function to spread it through the entire program/script. A very simple example would be the "insert a line" function simply using the "echo" in bash/shell script.

# space - Inserts an empty space. Can also use an echo in the code, only if an
# overall change is to be done, one change only will be required if this function
# is used.
space () {
    MSG="space: Can't grab console"  
    echo ""
    errlvl=$?
    errors
}

Note that we check the error-level (sometimes, on cannot write to the console, and in that case it won't make sense to execute the script anyway).

But one can also write more complex functions. Below example show 3 functions:

  • Separator - which will add a separator line

    ##############################################################################
    # separator - will add a separator line, 80 times =
    separator () {
      MSG="separator: Can't grab console"
      echo "================================================================================" 
      errlvl=$?
      errors
    }​
  • entry - which will enter the text into one line, and fill the missing entries with "=" signs to fill out a 80 char line

    ##############################################################################
    # entry - will add a text entry and format what it can.
    entry () {
    
      # Set to
      line=""
      lg=`echo $* | wc -c`
      let length=($lg + 8)
      while [ $length -lt 80 ]; 
      do
          line="${line}="
          let length=($length + 1)
      done
    
      # Add a space
      space
      MSG="entry: Can't grab console"
      echo "=== $* !  $line"
      errlvl=$?
      errors
    }
  • Title - which will as the entry, fill the line and make sure there is a separator in front and at the end.

    ##############################################################################
    # title - will Embedd the title in between 2 lines.
    title () {
      # Set to
      line=""
      lg=`echo $* | wc -c`
      let length=($lg + 4)
      while [ $length -lt 80 ]; 
      do
          line="${line}="
          let length=($length + 1)
      done
    
      space
      space
      # Note - we will skip the error handler here. If we can't grab the
      # console, the space function will bail out first anyway.
      echo "==============================================================================="
      echo "== $* $line"
      echo "==============================================================================="
      echo
    }

Example

When we now include and use these functions in our sample.sh script, we would need to replace the executable part with the following:

#######################################################################
# Actual script - do not modify anything below this point
#######################################################################

# Prevent double execution
Lock

# One way of using the log-message, put it all into the current MSG
# variable and invoque internal funciton "log"
MSG="Starting program $PROGNAME"
title $LDATE $MSG
#log $MSG
entry "Execution output below"

#sleep 30

if [ -f file.ba ]
then
    # Apply error message assuming the worst-case.
    MSG="Move file.ba to /tmp/foo.ba failed"
    mv -f file.ba /tmp/foo.ba 2>/dev/null
    # Assign the return code of the last program execution
    errlvl=$?
    errors

    # Echo the error code back to the console so we can see it.
    MSG="Move file.ba to /tmp/foo.ba Succeeded"

else
    entry "WARNING: File does not exist"
    echo -n " => File file.ba does not exist. Proceed [y/n]: "
    read YesNo

    if [ "$YesNo" == "y" ]
    then
    entry "Execution can continue after user choice"
    echo -n " => Please provide the admin password for the DB: "
    read -s PWD
    echo

    else
    MSG="file.ba does not exist"
    errlvl=1
    errors
    fi

fi

MSG="Continuing without file.ba"
# Log the operation. In case an error occured, we exit anyway
log $MSG
space
# remove lock-file
Unlock​

Executing the script would now provide us the following output:

jmertin@calypso:~/work/2020/Blogs$ ./sample.sh 


===============================================================================
== 2020-02-26 @ 17:05:50 Starting program sample ==============================
===============================================================================


=== Execution output below !  =================================================
================================================================================

=== WARNING: File does not exist !  ===========================================
 => File file.ba does not exist. Proceed [y/n]: y

=== Execution can continue after user choice !  ===============================
 => Please provide the admin password for the DB: 

=== Program execution finished !  =============================================​

Conclusion

The UI functions help to keep the same layout structure for interactivity with the user. Note that it will not influence the logging, so if one wants to add information to the logfiles, one needs to add these requests specifically.

CODE Github

The code has been stored on Github for those who just want to grab it: https://github.com/jmertin/shellscripting



0 comments
22 views

Permalink