feat(TODO.org):Add TODO list
[BK-2020-03.git] / unitproc / bktemplate
1 #!/bin/bash
2
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 ).
6
7 #== Variable Initialization ==
8
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.
17
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
22
23 #== Function Definitions ==
24
25
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 $*"; }
29
30 echoerr() {
31 # Usage: echo [ arguments ]
32 # Description: Prints provided arguments to stderr.
33 # Input: unspecified
34 # Output: 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
39 # License: GPLv3+
40 # Ref./Attrib:
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.
42
43 echo "$@" 1>&2; # Define stderr echo function. See [1].
44 return 0; # Function finished.
45 } # Define stderr message function.
46 vbm() {
47 # Usage: vbm "DEBUG:verbose message here"
48 # Description: Prints verbose message ("vbm") to stderr if OPTION_VERBOSE is set to "true".
49 # Input:
50 # - OPTION_VERBOSE variable set by processArguments function. (ex: "true", "false")
51 # - "$@" positional arguments fed to this function.
52 # Output: stderr
53 # Script function dependencies: echoerr
54 # External function dependencies: echo
55 # Last modified: 2020-04-11T23:57Z
56 # Last modified by: Steven Baltakatei Sandoval
57 # License: GPLv3+
58 # Ref./Attrib:
59
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.
63 fi
64
65 # End function
66 return 0; # Function finished.
67 } # Verbose message display function.
68 run() {
69 # Usage: run [cmd]
70 # Description: Runs command; reports success if command exit code 0; exits otherwise.
71 # Input: none
72 # Output: stdout
73 # Script function dependencies: none
74 # Last modified: 2020-06-19T22:09Z
75 # Last modified by: Steven Baltakatei Sandoval
76 # License: GPLv3+
77 # Ref.Attrib: https://stackoverflow.com/a/18880585
78 cmd_output=$(eval $1)
79 return_value=$?
80 if [ $return_value != 0 ]; then
81 echo "Command $1 failed";
82 exit -1;
83 else
84 echo "output: $cmd_output";
85 echo "Command succeeded.";
86 fi
87 return $return_value
88 } #
89 showUsage() {
90 # Usage: showUsage
91 # Description: Displays script usage information.
92 # Input: none
93 # Output: stderr
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
98 # License: GPLv3+
99 # Ref./Attrib.:
100
101 echoerr "USAGE:"
102 echoerr " bktemplate.sh [ options ]"
103 echoerr
104 echoerr "OPTIONS:"
105 echoerr " -h, --help"
106 echoerr " Display help information."
107 echoerr
108 echoerr " --version"
109 echoerr " Display script version."
110 echoerr
111 echoerr " -v, --verbose"
112 echoerr " Display debugging info."
113 echoerr
114 echoerr " -o, --output-file [ file ]"
115 echoerr " Specify output file."
116 echoerr
117 echoerr " -i, --input-file [ file ]"
118 echoerr " Specify input file."
119 echoerr
120 echoerr " -o, --output-dir [ directory ]"
121 echoerr " Specify output directory."
122 echoerr
123 echoerr " -i, --input-dir [ directory ]"
124 echoerr " Specify input directory."
125 echoerr
126
127 # End function
128 return 0; # Function finished.
129 } # Display information on how to use this script.
130 showVersion() {
131 # Usage: showVersion
132 # Descriptoin: Displays script version and license information.
133 # Input: unspecified
134 # Output: stderr
135 # Script function dependencies: echoerr
136 # External function dependencies: echo
137 # Last modified: 2020-04-11T23:57Z
138 # Last modified by: Steven Baltakatei Sandoval
139 # License: GPLv3+
140 # Ref./Attrib:
141
142 # Initialize function
143 vbm "DEBUG:showVersion function called."
144
145 # Perform work
146 OUTPUT="$SCRIPT_VERSION"
147
148 # Display results
149 echoerr "$OUTPUT";
150
151 # End function
152 vbm "DEBUG:showVersion function ended."
153 return 0; # Function finished.
154 } # Display script version.
155 processArguments() {
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
174 # License: GPLv3+
175 # Ref./Attrib.:
176 # [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347
177
178 # Initialize function
179 #vbm "DEBUG:processArguments function called."
180
181 # Perform work
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].
185 case "$1" in
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.
191 FILEIN1="$2";
192 shift;
193 vbm "DEBUG:Input file FILEIN1 set to:""$2";
194 else
195 echoerr "ERROR: Specified input file does not exist:""$2";
196 echoerr "Exiting.";
197 exit 1;
198 fi ;;
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.
201 DIRIN1="$2";
202 shift;
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";
206 echoerr "Exiting.";
207 exit 1;
208 fi ;;
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):"
212 read m; case $m in
213 y | Y | yes) OPTION_FILEOUT1_OVERWRITE="true";;
214 n | N | no) OPTION_FILEOUT1_OVERWRITE="false";;
215 *) echoerr "Invalid selection. Exiting."; exit 1;;
216 esac
217 if [ "$OPTION_FILEOUT1_OVERWRITE" == "true" ]; then
218 FILEOUT1="$2";
219 shift;
220 vbm "DEBUG:Output file FILEOUT1 set to:""$2";
221 else
222 echoerr "ERORR:Exiting in order to not overwrite output file:""$FILEOUT1";
223 exit 1;
224 fi
225 else
226 FILEOUT1="$2";
227 shift;
228 vbm "DEBUG:Output file FILEOUT1 set to:""$2";
229 fi ;;
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
232 DIROUT1="$2";
233 shift;
234 vbm "DEBUG:Output directory DIROUT1 set to:""$2";
235 else
236 echoerr "ERROR:Specified output directory is not valid:""$2";
237 echoerr "Exiting.";
238 exit 1;
239 fi ;;
240 *) echoerr "ERROR: Unrecognized argument."; exit 1;; # Handle unrecognized options. See [1].
241 esac
242 shift
243 done
244
245 # End function
246 vbm "DEBUG:processArguments function ended."
247 return 0; # Function finished.
248 } # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.).
249 checkExecutables() {
250 # Usage: checkExecutables [ command1 ] [ command2 ] [...] [ commandN ]
251 # Description: Checks that provided commands exist and displays those that do not exist.
252 # Input:
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
262 # License: GPLv3+
263 # Ref./Attrib.:
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.
268
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.
279
280 # Perform work
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."
285 else
286 outputInvalidCommandsArray+=("$candidateCommandName") ; # See [2].
287 vbm "DEBUG:Adding $candidateCommandName to outputInvalidCommandsArray."
288 fi
289 done
290
291 # Output results
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
294 exit 1; # See [1].
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
297 return 0; # See [1].
298 else
299 echoerr "ERROR: Check outputInvalidCommandsArray.";
300 fi
301
302 # End function
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.
309 # Input: (none)
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)
317 # ISO-861 format.
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
326 # License: GPLv3+
327 # Ref./Attrib.:
328
329 # Initialize function
330 vbm "DEBUG:updateTimeConstants function called."
331
332 # Perform work
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") ))" ;
340
341 # End function
342 vbm "DEBUG:updateTimeConstants function ended."
343 return 0; # Function finished.
344 } # Update time constants
345 checkapp() {
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'
350 local returnState
351 #echo "DEBUG:$(date +%S.%N)..Starting checkapp function."
352 #echo "DEBUG:args: $*"
353 #echo "DEBUG:returnState:$returnState"
354
355 #===Process Args===
356 for arg in "$@"; do
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
362 else
363 appRollCall[$arg]="false"; returnState="false";
364 fi
365 done
366
367 #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done
368 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
369
370 #===Determine function return code===
371 if [ "$returnState" = "true" ]; then
372 #echo "DEBUG:checkapp returns true for $arg";
373 return 0;
374 else
375 #echo "DEBUG:checkapp returns false for $arg";
376 return 1;
377 fi
378 } # Check that app exists
379 checkfile() {
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
385 local returnState
386
387 #===Process Args===
388 for arg in "$@"; do
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
394 else
395 fileRollCall["$arg"]="false"; returnState="false";
396 fi
397 done
398
399 #for key in "${!fileRollCall[@]}"; do echo "DEBUG:fileRollCall key [$key] is:${fileRollCall[$key]}"; done
400 #echo "DEBUG:evaluating returnstate. returnState:"$returnState
401
402 #===Determine function return code===
403 if [ "$returnState" = "true" ]; then
404 #echo "DEBUG:checkapp returns true for $arg";
405 return 0;
406 else
407 #echo "DEBUG:checkapp returns false for $arg";
408 return 1;
409 fi
410 } # Check that file exists
411
412
413 #== Main Program Definition ==
414 main() {
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
427 # License: GPLv3+
428 # Ref./Attrib.:
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.
430
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.
436
437 # Process inputs
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""========" ;
445 fi
446 fi
447
448 # Perform work
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";
452 fi
453 OUTPUT="$OUTPUT""The current time is:""$SCRIPT_TIME_SHORT\n"
454
455 # Output results
456 echo -e "$OUTPUT"
457
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" ;
468 fi
469
470 # End function
471 vbm "DEBUG:main function ended."
472 return 0; # Function finished.
473 }
474
475 #== Perform work and exit ==
476 main "$@" # Run main function.
477 exit 0;