Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Note that in the non-0 exit code case, the program may also report error information on standard error (e.g. ls: cannot access not_a_file: No such file or directory above).

Tip
titleTip

The $? return code variable must be checked immediately after the called program or sub-shell completes, because any further actions in the caller will change $?. One way to do this is to save off the value $? of in another variable (e.g. res=$?).

...

Code Block
languagebash
#!/bin/bash

# Script version global variable. Edit this whenever changes are made.
__ADVANCED_BASH_VERSION__="step_03"

# =======================================================================
#  Helper functions
# =======================================================================

# Shorter format date
date2() { date '+%Y-%m-%d %H:%M'; }

# Echo's its arguments and the date to std error
echo_se() { echo "$@ - `date2`" 1>&2; }
maybe_echo() {
  local do_echo=${ECHO_VERBOSE:-1}
  if [[ "$do_echo" == "1" ]]; then echo_se "$@"; fi
}

# Sets up auto-logging to a log file in the current directory
# using the specified logFileTag (arg 1) in the log file name.
auto_log() {
  local logFileTag="$1"
  if [[ "$logFileTag" != "" ]]; then
    local logFilePath="./autoLog_${logFileTag}.log"
    maybe_echo ".. logging to $logFilePath"
    exec 1> >(tee "$logFilePath") 2>&1
    res=$?
    if [[ "$res" != "0" ]]; then
      echo_se "** ERROR: auto logging returned non-0 exit code $res"
      exit 255
    fi
  else
    echo_se "** ERROR in auto_log: no logFile argument provided"
    exit 255
  fi
}

# =======================================================================
#  Command processing functions
# =======================================================================

# function that says "Hello World!" and displays user-specified text.
function helloWorld() {
  local txt1=$1
  local txt2=$2
  shift; shift
  local rest=$@
  echo "Hello World!"
  echo "  text 1: '$txt1'"
  echo "  text 2: '$txt2'"
  echo "  rest:   '$rest'"
}

# function that displays its 1st argument on standard output and
# its 2nd argument on standard error
function stdStreams() {
  local outTxt=${1:-"text for standard output"}
  local errTxt=${2:-"text for standard error"}
  echo    "to standard output: '$outTxt'"
  echo_se "to standard error:  '$errTxt'"
}

# function that illustrates auto-logging and capturing function output
#  arg 1 - (required) tag to identify the logfile
#  arg 2 - (optional) text for standard output
#  arg 3 - (optional) text for standard error
function testAutolog() {
  local logFileTag="$1"
  local outTxt=${2:-"text for standard output"}
  local errTxt=${3:-"text for standard error"}

  auto_log "$logFileTag"

  echo -e "\n1) Call stdStreams with output and error text:"
  stdStreams "$outTxt" "$errTxt"

  echo -e "\n2) Capture echo output in a variable and display it:"
  local output=`echo $outTxt`
  echo -e "   echo output was:\n$output"

  echo -e "\n3) Call echo_se with error text:"
  echo_se "$errTxt"

  echo -e "\n4)Capture echo_se function output in a variable and display it:"
  output=`echo_se "$errTxt"`
  echo -e "echo_se output was: '$output'"
}

# =======================================================================
#  Main script command-line processing
# =======================================================================

function usage() {
  echo "
advanced_bash.sh, version $__ADVANCED_BASH_VERSION__

Usage: advanced_bash.sh <command> [arg1 arg2...]

Commands:
  helloWorld [text to display]
  stdStreams [text for stdout] [text for stderr]
  testAutolog <logFileTag> [text for stdout] [text for stderr]
"
  exit 255
}

CMD=$1    # initially $1 will be the command
shift     # after "shift", $1 will be the 2nd command-line argument; $2 the 3rd, etc.
          # and $@ will be arguments 2, 3, etc.
# Only show usage if there is a command argument,
# making it possible to source this file
if [[ "$CMD" != "" ]]; then
  case "$CMD" in
    helloWorld) helloWorld "$@"
      ;;
    stdStreams) stdStreams "$1" "$2"
      ;;
    testAutolog) testAutolog "$1" "$2" "$3"
      ;;
    *) usage
      ;;
  esac
fi

The Parts

modified command argument processing

To allow our script to be source'd, top-level command argument processing has been modified so that the usage function (which calls exit) is only called if there is a command argument provided.

...

languagebash

...

date2 and maybe_echo functions

The echo_se function has been modified to call a new date2 function, which calls date specifying a custom, shorter date format.

We've also added a maybe_echo function that calls echo_se if the user wants verbose messages (which is the default, based on the ECHO_VERBOSE environment variable, but that the user can change by export'ing a different value to the script).

Code Block
languagebash
# Shorter format date
date2() { date '+%Y-%m-%d %H:%M'; }

# Echo's its arguments and the date to std error
echo_se() { echo "$@ - `date2`" 1>&2; }
maybe_echo() {
  local do_echo=${ECHO_VERBOSE:-1}
  if [[ "$do_echo" == "1" ]]; then echo_se  case "$CMD$@"; in
fi
}

modified command argument processing

To allow our script to be source'd, top-level command argument processing has been modified so that the usage function (which returns a non-0 exit code with exit 255) is only called if there is a command argument provided.

Code Block
languagebash
CMD=$1    helloWorld)# helloWorldinitially "$@"$1 will be the command
shift  ;;   # after stdStreams) stdStreams "$1shift", "$2"$1 will be the 2nd command-line  argument;; $2 the 3rd, etc.
testAutolog) testAutolog "$1" "$2" "$3"      # ;;and $@ will be arguments *) usage
      ;;
  esac
fi

So we only see usage if we type something after the script name:

Code Block
languagebash
# Does not show usage
~/workshop/step_03.sh

# Shows usage
~/workshop/step_03.sh x

date2 and maybe_echo functions

The echo_se function has been modified to call a new date2 function, which calls date specifying a custom, shorter date format.

We've also added a maybe_echo function that calls echo_se if the user wants verbose messages (which is the default, based on the ECHO_VERBOSE environment variable, but that the user can change by export'ing a different value to the script).

Code Block
languagebash
# Shorter format date
date2() { date '+%Y-%m-%d %H:%M'; }

# Echo's its arguments and the date to std error
echo_se() { echo "$@ - `date2`" 1>&2; }
maybe_echo() {
  local do_echo=${ECHO_VERBOSE:-1}
  if [[ "$do_echo" == "1" ]]; then echo_se "$@"; fi
}2, 3, etc.
# Only show usage if there is a command argument,
# making it possible to source this file
if [[ "$CMD" != "" ]]; then
  case "$CMD" in
    helloWorld) helloWorld "$@"
      ;;
    stdStreams) stdStreams "$1" "$2"
      ;;
    testAutolog) testAutolog "$1" "$2" "$3"
      ;;
    *) usage
      ;;
  esac
fi

So we only see usage if we type something after the script name:

Code Block
languagebash
# Does not show usage
~/workshop/step_03.sh

# Shows usage
~/workshop/step_03.sh x

testing source'd functions

...