diff --git a/bin/foamMonitor b/bin/foamMonitor
index a15bd5b168ffc43c125cf7ca03c5082e77e47e0f..5a4f736a0bab73eefcd1302784b1c34325e97fad 100755
--- a/bin/foamMonitor
+++ b/bin/foamMonitor
@@ -7,7 +7,7 @@
 #    \\/     M anipulation  |
 #-------------------------------------------------------------------------------
 #    Copyright (C) 2015 OpenFOAM Foundation
-#    Copyright (C) 2019 OpenCFD Ltd.
+#    Copyright (C) 2019-2021 OpenCFD Ltd.
 #------------------------------------------------------------------------------
 # License
 #     This file is part of OpenFOAM.
@@ -34,35 +34,49 @@
 #     - requires gnuplot, gnuplot_x11
 #
 #------------------------------------------------------------------------------
-usage() {
-    exec 1>&2
-    while [ "$#" -ge 1 ]; do echo "$1"; shift; done
+printHelp() {
     cat<<USAGE
 
-Usage: ${0##*/} [OPTION] <file>
-options:
-  -h | -help            prints the usage
-  -i | -idle <time>     stops if <file> unchanging for <time> sec (default = 60)
-  -l | -logscale        plots data (y-axis) on log scale, e.g. for residuals
-  -r | -refresh <time>  refreshes display every <time> sec (default = 10)
-  -y | -yrange <range>  sets data (y-axis) <range>, format "[0:1]"
-  -g | -grid            draws grid lines on the plot
+Usage: ${0##*/} [OPTIONS] <file>
+Options:
+  -g | -grid            Draw grid lines
+  -i | -idle <time>     Stop if <file> unchanging for <time> sec (default = 60)
+  -l | -logscale        Plot y-axis data on log scale
+  -r | -refresh <time>  Refresh display every <time> sec (default = 10)
+  -x | -xrange <range>  Set <range> of x-axis data, format "[0:1]"
+  -y | -yrange <range>  Set <range> of y-axis data, format "[0:1]"
+  -h | -help            Display short help and exit
 
 Monitor data with Gnuplot from time-value(s) graphs written by OpenFOAM
-e.g. by functionObjects
-- requires gnuplot, gnuplot_x11
+e.g. by functionObjects. For example,
 
-Example:
-  foamMonitor -l postProcessing/residuals/0/residuals.dat
+    foamMonitor -l postProcessing/residuals/0/residuals.dat
 
 USAGE
+    exit 0  # A clean exit
+}
+
+
+# Report error and exit
+die()
+{
+    exec 1>&2
+    echo
+    echo "Error encountered:"
+    while [ "$#" -ge 1 ]; do echo "    $1"; shift; done
+    echo
+    echo "See '${0##*/} -help' for usage"
+    echo
     exit 1
 }
 
+
+# Set Gnuplot header
 plotFileHeader() {
     cat<<EOF
 set term x11 1 font "helvetica,17" linewidth 1.5 persist noraise
 $LOGSCALE
+$XRANGE
 $YRANGE
 $GRID
 set title "Data Monitoring"
@@ -71,34 +85,49 @@ plot \\
 EOF
 }
 
+
+# Set Gnuplot footer
 plotFileFooter() {
     cat<<EOF
-
 pause $REFRESH
 reread
 EOF
 }
 
-howMany() ( set -f; set -- $1; echo $# )
 
+# Count number of tokens in a variable
+howMany() {
+    ( set -f; set -- $1; echo $# )
+}
+
+
+#-------------------------------------------------------------------------------
 IDLE=60
 REFRESH=10
 LOGSCALE=""
+XRANGE=""
 YRANGE=""
 GRID=""
 GNUPLOT=$(which gnuplot)
-! [ "x$GNUPLOT" = "x" ] || usage "Gnuplot not installed"
+[ ! "$GNUPLOT" = "" ] || die "foamMonitor requires Gnuplot installed"
 
-# parse options
+#-------------------------------------------------------------------------------
+
+# Parse options
 while [ "$#" -gt 0 ]
 do
     case "$1" in
     -h | -help*)
-        usage
+        printHelp
         ;;
     -i | -idle)
-        [ "$#" -ge 2 ] || usage "'$1' option requires an argument"
-        [ ! -z "${2##*[!0-9]*}" ] && IDLE=$2 || usage "Argument of '$1' is not an integer: '$2'"
+        [ "$#" -ge 2 ] || die "'$1' option requires an argument"
+        if [ -n "${2##*[!0-9]*}" ]
+        then
+            IDLE=$2
+        else
+            die "Argument of '$1' is not an integer: '$2'"
+        fi
         shift 2
         ;;
     -l | -logscale)
@@ -106,12 +135,22 @@ do
         shift 1
         ;;
     -r | -refresh)
-        [ "$#" -ge 2 ] || usage "'$1' option requires an argument"
-        [ ! -z "${2##*[!0-9]*}" ] && REFRESH=$2 || usage "Argument of '$1' is not an integer: '$2'"
+        [ "$#" -ge 2 ] || die "'$1' option requires an argument"
+        if [ -n "${2##*[!0-9]*}" ]
+        then
+            REFRESH=$2
+        else
+            die "Argument of '$1' is not an integer: '$2'"
+        fi
+        shift 2
+        ;;
+    -x | -xrange)
+        [ "$#" -ge 2 ] || die "'$1' option requires an argument"
+        XRANGE="set xrange $2"
         shift 2
         ;;
     -y | -yrange)
-        [ "$#" -ge 2 ] || usage "'$1' option requires an argument"
+        [ "$#" -ge 2 ] || die "'$1' option requires an argument"
         YRANGE="set yrange $2"
         shift 2
         ;;
@@ -120,7 +159,7 @@ do
         shift 1
         ;;
     -*)
-        usage "unknown option: '$*'"
+        die "unknown option: '$*'"
         ;;
     *)
         break
@@ -128,28 +167,28 @@ do
     esac
 done
 
-[ $# -eq 1 ] || usage "Incorrect arguments specified"
-[ -f $1 ]    || usage "File $1 does not exit"
-FILE=$1
+[ "$#" -eq 1 ] || die "Incorrect arguments specified"
+[ -f "$1" ]    || die "File $1 does not exit"
+FILE="$1"
 
 # Get KEYS from header
-KEYS=$(grep -E '^#' $FILE | tail -1)
+KEYS=$(grep -E '^#' "$FILE" | tail -1)
 
-[ "x$KEYS" = "x" ] && KEYS="# Step"
+[ "$KEYS" = "" ] && KEYS="# Step"
 NKEYS=$(howMany "$KEYS")
-NCOLS=$(tail -1 $FILE | awk '{ print NF}')
+NCOLS=$(grep -m 1 '^[^#]' "$FILE" | awk '{ print NF }')
 
 # With full column labels, NKEYS = NCOLS + 1, since it includes "#"
 
 # If NKEYS > NCOLS + 1, REMOVE EXCESS KEYS
-NCOLSPONE=$(expr $NCOLS + 1)
+NCOLSPONE=$((NCOLS+1))
 [ "$NKEYS" -gt "$NCOLSPONE" ] && KEYS=$(echo $KEYS | cut -d" " -f1-$NCOLSPONE)
 NKEYS=$(howMany "$KEYS")
 
 i=0
 while [ "$NKEYS" -le "$NCOLS" ]
 do
-    i=$(expr $i + 1)
+    i=$((i+1))
     KEYS="$KEYS data$i"
     NKEYS=$(howMany "$KEYS")
 done
@@ -159,33 +198,33 @@ XLABEL=$(echo $KEYS | cut -d " " -f2)
 KEYS=$(echo $KEYS | cut -d " " -f3-)
 
 GPFILE=$(mktemp)
-plotFileHeader > $GPFILE
+plotFileHeader > "$GPFILE"
 i=1
 for field in $KEYS
 do
-    i=$(expr $i + 1)
+    i=$((i+1))
     PLOTLINE="\"$FILE\" using 1:${i} with lines title \"$field\""
     if [ $i -lt $NCOLS ]
     then
        PLOTLINE="$PLOTLINE, \\"
     fi
-    echo $PLOTLINE >> $GPFILE
+    echo $PLOTLINE >> "$GPFILE"
 done
-plotFileFooter >> $GPFILE
+plotFileFooter >> "$GPFILE"
 
-touch $FILE
-$GNUPLOT $GPFILE &
+touch "$FILE"
+$GNUPLOT "$GPFILE" &
 PID=$!
 
 while true
 do
     MODTIME=$(stat --format=%Y $FILE)
-    IDLEAGO=$(expr $(date +%s) - $IDLE)
+    IDLEAGO=$(($(date +%s)-IDLE))
     test "$MODTIME" -gt "$IDLEAGO" || break
     sleep $REFRESH
 done
 
 kill -9 $PID
-rm $GPFILE
+rm -f "$GPFILE"
 
 #------------------------------------------------------------------------------