3 # Author: Steven Baltakatei Sandoval 
   4 # Description: Template for bash scripts. 
   5 # Note: Use hide-show-block function to aid readability. (ex: https://www.emacswiki.org/emacs/HideShow ). 
   7 #== Variable Initialization == 
   9 #== Global constants == 
  10 SCRIPT_TTL
=10                   # Limit script life to this in seconds. 
  11 PATH
="/usr/local/bin/:$PATH"    # Add user binary executable directory to PATH. 
  12 PATH
="/opt/bktei:$PATH"         # Add 'optional' executable directory to PATH. 
  13 SCRIPT_HOSTNAME
=$
(hostname
)     # Save hostname of system running this script. 
  14 SCRIPT_VERSION
="bktemplate.sh 0.0.0" # Define version of script. Used by function 'showVersion'. 
  15 SCRIPT_TIME_SHORT
="$(date +%Y%m%dT%H%M%S%z)" # Save current date & time in ISO-8601 format (YYYYmmddTHHMMSS+zzzz). 
  16 SCRIPT_DATE_SHORT
="$(date +%Y%m%d)"          # Save current date in ISO-8601 format. 
  18 #==BEGIN Define script parameters 
  19 declare -Ag appRollCall 
# Associative array for storing app status (function checkapp()) 
  20 declare -Ag fileRollCall 
# Associative array for storing file status (function checkfile()) 
  21 #==END Define script parameters 
  23 #== Function Definitions == 
  26 yell
() { echo "$0: $*" >&2; } # Yell, Die, Try Three-Fingered Claw technique; # Ref/Attrib: https://stackoverflow.com/a/25515370 
  27 die
() { yell 
"$*"; exit 111; } 
  28 try
() { "$@" || die 
"cannot $*"; } 
  31     # Usage: echo [ arguments ] 
  32     # Description: Prints provided arguments to stderr. 
  35     # Script function dependencies: none 
  36     # External function dependencies: echo 
  37     # Last modified: 2020-04-11T21:16Z 
  38     # Last modified by: Steven Baltakatei Sandoval 
  41     #  [1]: # Roth, James (2010-06-07). ["How to print text to stderr instead of stdout"](https://stackoverflow.com/a/2990533). Licensed CC BY-SA 4.0. 
  43     echo "$@" 1>&2; # Define stderr echo function. See [1]. 
  44     return 0; # Function finished. 
  45 } # Define stderr message function. 
  47     # Usage: vbm "DEBUG:verbose message here" 
  48     # Description: Prints verbose message ("vbm") to stderr if OPTION_VERBOSE is set to "true". 
  50     #   - OPTION_VERBOSE  variable set by processArguments function. (ex: "true", "false") 
  51     #   - "$@"            positional arguments fed to this function. 
  53     # Script function dependencies: echoerr 
  54     # External function dependencies: echo 
  55     # Last modified: 2020-04-11T23:57Z 
  56     # Last modified by: Steven Baltakatei Sandoval 
  60     if [ "$OPTION_VERBOSE" = "true" ]; then 
  61         FUNCTION_TIME
=$
(date --iso-8601=ns
); # Save current time in nano seconds. 
  62         echoerr 
"[$FUNCTION_TIME] ""$*"; # Display argument text. 
  66     return 0; # Function finished. 
  67 } # Verbose message display function. 
  70     # Description: Runs command; reports success if command exit code 0; exits otherwise. 
  73     # Script function dependencies: none 
  74     # Last modified: 2020-06-19T22:09Z 
  75     # Last modified by: Steven Baltakatei Sandoval 
  77     # Ref.Attrib: https://stackoverflow.com/a/18880585 
  80     if [ $return_value != 0 ]; then 
  81         echo "Command $1 failed"; 
  84         echo "output: $cmd_output"; 
  85         echo "Command succeeded."; 
  91     # Description: Displays script usage information. 
  94     # Script function dependencies: echoerr 
  95     # External dependencies: bash (5.0.3), echo 
  96     # Last modified: 2020-05-04T16:11Z 
  97     # Last modified by: Steven Baltakatei Sandoval 
 102     echoerr 
"    bktemplate.sh [ options ]" 
 105     echoerr 
"    -h, --help" 
 106     echoerr 
"            Display help information." 
 109     echoerr 
"            Display script version." 
 111     echoerr 
"    -v, --verbose" 
 112     echoerr 
"            Display debugging info." 
 114     echoerr 
"    -o, --output-file [ file ]" 
 115     echoerr 
"            Specify output file." 
 117     echoerr 
"    -i, --input-file [ file ]" 
 118     echoerr 
"            Specify input file." 
 120     echoerr 
"    -o, --output-dir [ directory ]" 
 121     echoerr 
"            Specify output directory." 
 123     echoerr 
"    -i, --input-dir [ directory ]" 
 124     echoerr 
"            Specify input directory." 
 128     return 0; # Function finished. 
 129 } # Display information on how to use this script. 
 132     # Descriptoin: Displays script version and license information. 
 135     # Script function dependencies: echoerr 
 136     # External function dependencies: echo 
 137     # Last modified: 2020-04-11T23:57Z 
 138     # Last modified by: Steven Baltakatei Sandoval 
 142     # Initialize function 
 143     vbm 
"DEBUG:showVersion function called." 
 146     OUTPUT
="$SCRIPT_VERSION" 
 152     vbm 
"DEBUG:showVersion function ended." 
 153     return 0; # Function finished. 
 154 } # Display script version. 
 156     # Usage: processArguments "$@" 
 157     # Description: Processes provided arguments in order to set script option variables useful for 
 158     #   changing how other functions behave. For example, it may: 
 159     #   1. Activate verbose mode 
 160     # Input: "$@"          (list of arguments provided to the function) 
 161     # Output: Sets following variables used by other functions: 
 162     #   OPTION_VERBOSE     Indicates verbose mode enable status.  (ex: "true", "false") 
 163     #   DIROUT1            Path to output directory. 
 164     #   FILEOUT1           Path to output file. 
 165     #   DIRIN1             Path to input directory. 
 166     #   FILEIN1            Path to input file. 
 167     #   OPTION_FILEOUT1_OVERWRITE Indicates whether file FILEOUT1 should be overwritten (ex: "true", "false') 
 168     # Script function dependencies: 
 169     #   - echoerr          Displays messages to stderr. 
 170     #   - vbm              Displays messsages to stderr if OPTION_VERBOSE set to "true". 
 171     # External dependencies: bash (5.0.3), echo 
 172     # Last modified: 2020-05-04T14:41Z 
 173     # Last modified by: Steven Baltakatei Sandoval 
 176     #  [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347 
 178     # Initialize function 
 179     #vbm "DEBUG:processArguments function called." 
 182     while [ ! $# -eq 0 ]; do   # While number of arguments ($#) is not (!) equal to (-eq) zero (0). 
 183         #1>&2 echo "DEBUG:Starting processArguments while loop." # Debug stderr message. See [1]. 
 184         #1>&2 echo "DEBUG:Provided arguments are:""$*" # Debug stderr message. See [1]. 
 186             -h | 
--help) showUsage
; exit 1;; # Display usage. 
 187             --version) showVersion
; exit 1;; # Show version 
 188             -v | 
--verbose) OPTION_VERBOSE
="true"; vbm 
"DEBUG:Verbose mode enabled.";; # Enable verbose mode. See [1]. 
 189             -i | 
--input-file) # Define input file path 
 190                 if [ -f "$2" ]; then # If $2 is file that exists, set FILEIN1 to $2, pop $2. 
 193                     vbm 
"DEBUG:Input file FILEIN1 set to:""$2"; 
 195                     echoerr 
"ERROR: Specified input file does not exist:""$2"; 
 199             -I | 
--input-dir) # Define input directory path 
 200                 if [ -d "$2" ]; then # If $2 is dir that exists, set DIRIN1 to $2, pop $2. 
 203                     vbm 
"DEBUG:Input directory DIRIN1 set to:""$2"; 
 204                 else # Display error if $2 is not a valid dir. 
 205                     echoerr 
"ERROR:Specified input directory does not exist:""$2"; 
 209             -o | 
--output-file) # Define output file path 
 210                 if [ -f "$2" ]; then # If $2 is file that exists, prompt user to continue to overwrite, set FILEOUT1 to $2, pop $2. 
 211                     echoerr 
"Specified output file $2 already exists. Overwrite? (y/n):" 
 213                                 y | Y | 
yes) OPTION_FILEOUT1_OVERWRITE
="true";; 
 214                                 n | N | no
) OPTION_FILEOUT1_OVERWRITE
="false";; 
 215                                 *) echoerr 
"Invalid selection. Exiting."; exit 1;; 
 217                     if [ "$OPTION_FILEOUT1_OVERWRITE" == "true" ]; then 
 220                         vbm 
"DEBUG:Output file FILEOUT1 set to:""$2"; 
 222                         echoerr 
"ERORR:Exiting in order to not overwrite output file:""$FILEOUT1"; 
 228                     vbm 
"DEBUG:Output file FILEOUT1 set to:""$2"; 
 230             -O | 
--output-dir) # Define output directory path 
 231                 if [ -d "$2" ]; then # If $2 is dir that exists, set DIROUT1 to $2, pop $2 
 234                     vbm 
"DEBUG:Output directory DIROUT1 set to:""$2"; 
 236                     echoerr 
"ERROR:Specified output directory is not valid:""$2"; 
 240             *) echoerr 
"ERROR: Unrecognized argument."; exit 1;; # Handle unrecognized options. See [1]. 
 246     vbm 
"DEBUG:processArguments function ended." 
 247     return  0; # Function finished. 
 248 } # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.). 
 250     # Usage: checkExecutables [ command1 ] [ command2 ] [...] [ commandN ] 
 251     # Description: Checks that provided commands exist and displays those that do not exist. 
 253     #   - command names (arguments) 
 254     # Output: commands that don't exist (stderr) 
 255     # Script function dependencies: 
 256     #   - echoerr          for displaying errors via stderr 
 257     #   - processArguments for setting OPTION_VERBOSE 
 258     #   - vbm              for displaying verbose messages if OPTION_VERBOSE is "true" 
 259     # External dependencies: bash (5.0.3), command 
 260     # Last modified: 2020-04-11T23:59Z 
 261     # Last modified by: Steven Baltakatei Sandoval 
 264     #  [1]: SiegeX (2010-12-12). ["Difference between return and exit in Bash functions."](https://stackoverflow.com/a/4419971). Licensed CC BY-SA 4.0. 
 265     #  [2]: Darryl Hein (2009-12-23). ["Add a new element to an array without specifying the index in Bash"](https://stackoverflow.com/a/1951523). Licensed CC BY-SA 4.0. 
 266     #  [3]: kojiro (2012-10-03). ["Convert command line arguments into an array in Bash"](https://stackoverflow.com/a/12711853) 
 267     #  [4]: niieani (2016-01-12). ["How to copy an array in Bash?"](https://stackoverflow.com/a/34733375/10850071). Licensed CC BY-SA 4.0. 
 269     # Initialize function 
 270     vbm 
"DEBUG:checkExecutables function called." 
 271     declare -a candidateCommandsNames 
# Initialize array for storing positional arguments provided to this function. 
 272     candidateCommandsNames
=("$@") # Save positional arguments to variable as string. See [3]. 
 273     vbm 
"DEBUG:candidateCommandsNames:""$*" 
 274     vbm 
"DEBUG:candidateCommandsNames[0]:""${candidateCommandsNames[0]}" 
 275     vbm 
"DEBUG:candidateCommandsNames[1]:""${candidateCommandsNames[1]}" 
 276     vbm 
"DEBUG:candidateCommandsNames[2]:""${candidateCommandsNames[2]}" 
 277     declare -a outputInvalidCommandsArray 
# Initialize arary for storing names of invalid commands. 
 278     declare -a outputValidCommandsArray 
# Initialize array for storing names of valid commands. 
 281     for candidateCommandName 
in "${candidateCommandsNames[@]}"; do  # Search through all space-delimited text for valid commands. 
 282         if command -v "$candidateCommandName" 1>/dev
/null 
2>/dev
/null
; then # Check if a command is valid or not. 
 283             outputValidCommandsArray
+=("$candidateCommandName") ; # See [2]. 
 284             vbm 
"DEBUG:Adding $candidateCommandName to outputValidCommandsArray." 
 286             outputInvalidCommandsArray
+=("$candidateCommandName") ; # See [2]. 
 287             vbm 
"DEBUG:Adding $candidateCommandName to outputInvalidCommandsArray." 
 292     if [ ${#outputInvalidCommandsArray[@]} -gt 0 ]; then # if number of elements in outputInvalidCommandsArray greater than 0, then display offending commands and exit 1. 
 293         echoerr 
"ERROR: Invalid commands found:""${outputInvalidCommandsArray[@]}"; # display invalid commands as error 
 295     elif [ ${#outputInvalidCommandsArray[@]} -eq 0 ]; then # if number of elements in outputInvalidCommandsArray equals zero, then return 0. 
 296         vbm 
"DEBUG: Valid commands are:""${outputValidCommandsArray[@]}"; # display valid commands if verbose mode enabled 
 299         echoerr 
"ERROR: Check outputInvalidCommandsArray."; 
 303     vbm 
"DEBUG:checkExecutables function ended." 
 304     return 0; # Function finished. 
 305 } # Check that certain executables exist. 
 306 updateTimeConstants
() { 
 307     # Usage: updateTimeConstants 
 308     # Description: Updates time-related variables for use by other scripts. 
 310     # Output: Sets following variables: 
 311     #   TIME_CURRENT       Current time in long ISO-8601 format.  (ex: YYYY-mm-ddTHH:MM:SS+ZZZZ) 
 312     #   TIME_CURRENT_SHORT Current time in short ISO-8601 format. (ex: YYYYmmddTHHMMSS+ZZZZ) 
 313     #   DATE_CURRENT       Current date in ISO-8601 format.       (ex: YYYY-mm-dd) 
 314     #   DATE_CURRENT_SHORT Current date in short ISO-8601 format. (ex: YYYYmmdd) 
 315     #   DATE_TOMORROW      Tomorrow's date in ISO-8601 format.    (ex: YYYY-mm-dd) 
 316     #   TIME_NEXT_MIDNIGHT Time of tomorrow's midnight in long    (ex: YYYY-mm-ddTHH:MM:SS+ZZZZ) 
 318     #   SEC_TIL_MIDNIGHT   Seconds until next midnight. 
 319     # Script function dependencies: 
 320     #   - echoerr          for displaying errors via stderr 
 321     #   - processArguments for setting OPTION_VERBOSE 
 322     #   - vbm              for displaying verbose messages if OPTION_VERBOSE is "true" 
 323     # External dependencies: bash (5.0.3), date, echo 
 324     # Last modified: 2020-04-11T23:59Z 
 325     # Last modified by: Steven Baltakatei Sandoval 
 329     # Initialize function 
 330     vbm 
"DEBUG:updateTimeConstants function called." 
 333     TIME_CURRENT
="$(date --iso-8601=seconds)" ; 
 334     TIME_CURRENT_SHORT
="$(date -d "$TIME_CURRENT" +%Y%m%dT%H%M%S%z)" 
 335     DATE_CURRENT
="$(date -d "$TIME_CURRENT" --iso-8601=date)" ; 
 336     DATE_CURRENT_SHORT
="$(date -d "$TIME_CURRENT" +%Y%m%d)" ; 
 337     DATE_TOMORROW
="$(date -d "$TIME_CURRENT next day
" --iso-8601=date)" ; 
 338     TIME_NEXT_MIDNIGHT
="$(date -d "$DATE_TOMORROW" --iso-8601=seconds)" ; 
 339     SECONDS_UNTIL_NEXT_MIDNIGHT
="$((  $(date +%s -d "$TIME_NEXT_MIDNIGHT") - $(date +%s -d "$TIME_CURRENT")  ))" ; 
 342     vbm 
"DEBUG:updateTimeConstants function ended." 
 343     return 0; # Function finished. 
 344 } # Update time constants 
 346     # Desc: If arg is a command, save result in assoc array 'appRollCall' 
 347     # Usage: checkapp arg1 arg2 arg3 ... 
 348     # Input: global assoc. array 'appRollCall' 
 349     # Output: adds/updates key(value) to global assoc array 'appRollCall' 
 351     #echo "DEBUG:$(date +%S.%N)..Starting checkapp function." 
 352     #echo "DEBUG:args: $*" 
 353     #echo "DEBUG:returnState:$returnState" 
 357         #echo "DEBUG:processing arg:$arg" 
 358         if command -v "$arg" 1>/dev
/null 
2>&1; then # Check if arg is a valid command 
 359             appRollCall
[$arg]="true"; 
 360             #echo "DEBUG:appRollCall[$arg]:"${appRollCall[$arg]} 
 361             if ! [ "$returnState" = "false" ]; then returnState
="true"; fi 
 363             appRollCall
[$arg]="false"; returnState
="false"; 
 367     #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done 
 368     #echo "DEBUG:evaluating returnstate. returnState:"$returnState 
 370     #===Determine function return code=== 
 371     if [ "$returnState" = "true" ]; then 
 372         #echo "DEBUG:checkapp returns true for $arg"; 
 375         #echo "DEBUG:checkapp returns false for $arg"; 
 378 } # Check that app exists 
 380     # Desc: If arg is a file path, save result in assoc array 'fileRollCall' 
 381     # Usage: checkfile arg1 arg2 arg3 ... 
 382     # Input: global assoc. array 'fileRollCall' 
 383     # Output: adds/updates key(value) to global assoc array 'fileRollCall'; 
 384     # Output: returns 0 if app found, 1 otherwise 
 389         #echo "DEBUG:processing arg:$arg" 
 390         if [ -f "$arg" ]; then 
 391             fileRollCall
["$arg"]="true"; 
 392             #echo "DEBUG:fileRollCall[\"$arg\"]:"${fileRollCall["$arg"]} 
 393             if ! [ "$returnState" = "false" ]; then returnState
="true"; fi 
 395             fileRollCall
["$arg"]="false"; returnState
="false"; 
 399     #for key in "${!fileRollCall[@]}"; do echo "DEBUG:fileRollCall key [$key] is:${fileRollCall[$key]}"; done 
 400     #echo "DEBUG:evaluating returnstate. returnState:"$returnState 
 402     #===Determine function return code=== 
 403     if [ "$returnState" = "true" ]; then 
 404         #echo "DEBUG:checkapp returns true for $arg"; 
 407         #echo "DEBUG:checkapp returns false for $arg"; 
 410 } # Check that file exists 
 413 #== Main Program Definition == 
 415     # Usage: main "$@"    # See [1]. 
 416     # Input: unspecified (note: all Global Constants declared at beginning of script assumed to be available) 
 417     #   OPTION_VERBOSE   (used by vbm, set by processArguments)  (ex: "true", "false") 
 418     # Output: unspecified 
 419     # Script function dependencies: 
 420     #   - echoerr          for displaying errors via stderr 
 421     #   - vbm              for displaying verbose messages if OPTION_VERBOSE is "true" 
 422     #   - processArguments for setting OPTION_VERBOSE 
 423     #   - checkExecutables for checking that specified commands are available 
 424     # External dependencies: bash (5.0.3), echo 
 425     # Last modified: 2020-05-04T16:50Z 
 426     # Last modified by: Steven Baltakatei Sandoval 
 429     #  [1]: ErichBSchulz (2011-11-20). [How to correctly pass bash script arguments to functions](https://stackoverflow.com/a/8198970). Licensed CC BY-SA 4.0. 
 431     # Initialize function 
 432     processArguments 
"$@" # Process arguments. 
 433     vbm 
"DEBUG:main function called." 
 434     vbm 
"DEBUG:main function arguments are:""$@" 
 435     checkExecutables 
"echo" "date" "cut" "awk" # Confirm that executables necessary to run are available. Exit if any fail. 
 438     if [ -v FILEIN1 
]; then # VERBOSE: Display contents of FILEIN1 if FILEIN1 has been set. 
 439         vbm 
"DEBUG:main function detects input file:""$FILEIN1" ; 
 440         vbm 
"DEBUG:contents of following file is displayed below:""$FILEIN1" ; 
 441         if [ "$OPTION_VERBOSE" == "true" ]; then # display FILEIN1 contents via cat if verbose mode specified 
 442             echoerr 
"========""$FILEIN1"" START""========" ; 
 443             cat "$FILEIN1" 1>&2 ; 
 444             echoerr 
"========""$FILEIN1"" END""========" ; 
 449     OUTPUT
="$OUTPUT""Hello world.\n" 
 450     if [ -v FILEIN1 
]; then 
 451         OUTPUT
="$OUTPUT""The b2sum hash of $FILEIN1 is: $(b2sum "$FILEIN1" | awk '{print $1}').\n"; 
 453     OUTPUT
="$OUTPUT""The current time is:""$SCRIPT_TIME_SHORT\n" 
 458     if [ -v FILEOUT1 
]; then  # if FILEOUT1 set, write to this file. 
 459         vbm 
"DEBUG:main function detects output file:""$FILEOUT1" ; 
 460         echo -e "$OUTPUT" > "$FILEOUT1" ; 
 461         vbm 
"DEBUG:main funtion wrote OUTPUT to:""$FILEOUT1" ; 
 462     elif [ -v DIROUT1 
]; then  # else if DIROUT1 set, set FILEOUT1 to timestamped filename, combine into PATHOUT1, write output to PATHOUT1. 
 463         vbm 
"DEBUG:main function detects output directory:""$DIROUT1" ; 
 464         FILEOUT1
="$SCRIPT_TIME_SHORT"..output
 
 465         PATHOUT1
="$DIROUT1"/"$FILEOUT1" 
 466         echo -e "$OUTPUT" > $PATHOUT1 ; 
 467         vbm 
"DEBUG:main function wrote output file to:""$PATHOUT1" ; 
 471     vbm 
"DEBUG:main function ended." 
 472     return 0; # Function finished. 
 475 #== Perform work and exit == 
 476 main 
"$@" # Run main function.