Versions Compared

Key

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

...

What this section covers

  • Standard Unix streams, redirection and piping
  • How and why to direct script diagnostic messages to standard error, but function results to standard output
  • Argument defaulting
  • Automatically creating a log file for a script
  • Capturing standard output in a variable using backtick quoting

Standard streams

As described Intro Unix: Standard streams and redirection each with a well-defined stream number:

...

  • redirect standard output to a file, overwriting any exiting contents:
    echo "Output text" > out.txt
    echo "Some output" 1> out.txt
  • redirect standard output to a file, appending to any exiting contents:
    echo "Some text" >> out.txt
    echo "More text" 1>> out.txt
  • redirect standard error output to a file, overwriting any exiting contents:
    ls xxxx 2> err.txt
  • redirect standard error to standard output, and redirect standard output to a file:
    ls xxxx > ls.log 2>&1
  • redirect standard output to standard error:
    echo "Output that will be redirected to standard error" 1>&2
    echo "Output that will be redirected standard error" 2> err.txt 1>&2

...

For example, here's how to call the step_01.sh script so that its standard output goes to both the terminal Terminal and to a file.

Code Block
languagebash
~/workshop/step_01.sh helloWorld My name is Anna | tee step_01.log

See Standard streams and piping in the Intro Unix wiki.

When standard output and standard error streams are used

...

Here's a step_02.sh script that builds on our step_01.sh work. It is located at ~/workshop/step_02.sh. Make sure it is executable (chmod +x ~/workshop/step_02.txt).

Code Block
languagebash
#!/bin/bash

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

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

# Echo's its arguments to std error
echo_se() { echo "$@" 1>&2; }

# 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"
    echo_se ".. logging to $logFilePath"
    exec 1> >(tee "$logFilePath") 2>&1
  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.
case "$CMD" in
  helloWorld) helloWorld "$@"
    ;;
  stdStreams) stdStreams "$1" "$2"
    ;;
  testAutolog) testAutolog "$1" "$2" "$3"
    ;;
  *) usage
    ;;
esac

...

Here's how to call the stdStreams command specifying the text "hello world!" for standard output and "goodbye world!" for standard error.

Code Block
languagebash
~/workshop/step_02.sh stdStreams '"hello world!' "'goodbye world!"'

But how can you tell which stream is which? They're both written to the terminal Terminal by default!

exercise 1

Call the stdStreams command in some manner that can distinguish between standard output and standard error.

...