3 # Date: 2020-05-04T18:50Z 
   4 # Author: Steven Baltakatei Sandoval 
   5 # Description: Script to lookup file hash from existing hash list. 
   6 # Note: Use hide-show-block function to aid readability. (ex: https://www.emacswiki.org/emacs/HideShow ). 
   8 #== Variable Initialization == 
  10 #== Global constants == 
  11 SCRIPT_TTL
=10                   # Limit script life to this in seconds. 
  12 PATH
="/usr/local/bin/:$PATH"    # Add user binary executable directory to PATH. 
  13 PATH
="/opt/bktei:$PATH"         # Add 'optional' executable directory to PATH. 
  14 SCRIPT_HOSTNAME
=$
(hostname
)     # Save hostname of system running this script. 
  15 SCRIPT_VERSION
="bktemplate.sh 0.0.0" # Define version of script. Used by function 'showVersion'. 
  16 SCRIPT_TIME_SHORT
="$(date +%Y%m%dT%H%M%S%z)" # Save current date & time in ISO-8601 format (YYYYmmddTHHMMSS+zzzz). 
  17 SCRIPT_DATE_SHORT
="$(date +%Y%m%d)"          # Save current date in ISO-8601 format. 
  19 #== Function Definitions == 
  21     # Usage: echo [ arguments ] 
  22     # Description: Prints provided arguments to stderr. 
  25     # Script function dependencies: none 
  26     # External function dependencies: echo 
  27     # Last modified: 2020-04-11T21:16Z 
  28     # Last modified by: Steven Baltakatei Sandoval 
  31     #  [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. 
  33     echo "$@" 1>&2; # Define stderr echo function. See [1]. 
  34     return 0; # Function finished. 
  35 } # Define stderr message function. 
  37     # Usage: vbm "DEBUG:verbose message here" 
  38     # Description: Prints verbose message ("vbm") to stderr if OPTION_VERBOSE is set to "true". 
  40     #   - OPTION_VERBOSE  variable set by processArguments function. (ex: "true", "false") 
  41     #   - "$@"            positional arguments fed to this function. 
  43     # Script function dependencies: echoerr 
  44     # External function dependencies: echo 
  45     # Last modified: 2020-04-11T23:57Z 
  46     # Last modified by: Steven Baltakatei Sandoval 
  50     if [ "$OPTION_VERBOSE" == "true" ]; then 
  51         FUNCTION_TIME
=$
(date --iso-8601=ns
); # Save current time in nano seconds. 
  52         echoerr 
"[$FUNCTION_TIME] ""$@"; # Display argument text. 
  56     return 0; # Function finished. 
  57 } # Verbose message display function. 
  60     # Description: Displays script usage information. 
  63     # Script function dependencies: echoerr 
  64     # External dependencies: bash (5.0.3), echo 
  65     # Last modified: 2020-05-04T16:58Z 
  66     # Last modified by: Steven Baltakatei Sandoval 
  71     echoerr 
"    bklu [OPTIONS] [FILE]" 
  73     echoerr 
"    [FILE] hash is compared against file contents of directory" 
  74     echoerr 
"      specified by --input-dir option or environment variable" 
  75     echoerr 
"      BK_FILEHASHLIST_DIR ." 
  78     echoerr 
"    --digest-algo [ digest name ]" 
  79     echoerr 
"            Specify digest command name. Value should be GNU" 
  80     echoerr 
"            coreutils commands such as: sha256sum, md5sum, b2sum." 
  81     echoerr 
"            Default value is: b2sum" 
  84     echoerr 
"            Display help information." 
  87     echoerr 
"            Display script version." 
  89     echoerr 
"    -v, --verbose" 
  90     echoerr 
"            Display debugging info." 
  92     echoerr 
"    -o, --output-file [ file ]" 
  93     echoerr 
"            Specify output file." 
  95     echoerr 
"    -i, --input-file [ file ]" 
  96     echoerr 
"            Specify input file." 
  98     echoerr 
"    -o, --output-dir [ directory ]" 
  99     echoerr 
"            Specify output directory." 
 101     echoerr 
"    -i, --input-dir [ directory ]" 
 102     echoerr 
"            Specify input directory." 
 107     return 0; # Function finished. 
 108 } # Display information on how to use this script. 
 111     # Descriptoin: Displays script version and license information. 
 114     # Script function dependencies: echoerr 
 115     # External function dependencies: echo 
 116     # Last modified: 2020-04-11T23:57Z 
 117     # Last modified by: Steven Baltakatei Sandoval 
 121     # Initialize function 
 122     vbm 
"DEBUG:showVersion function called." 
 125     OUTPUT
="$SCRIPT_VERSION" 
 131     vbm 
"DEBUG:showVersion function ended." 
 132     return 0; # Function finished. 
 133 } # Display script version. 
 135     # Usage: processArguments "$@" 
 136     # Description: Processes provided arguments in order to set script option variables useful for 
 137     #   changing how other functions behave. For example, it may: 
 138     #   1. Activate verbose mode 
 139     # Input: "$@"          (list of arguments provided to the function) 
 140     # Output: Sets following variables used by other functions: 
 141     #   OPTION_VERBOSE     Indicates verbose mode enable status.  (ex: "true", "false") 
 142     #   DIROUT1            Path to output directory. 
 143     #   FILEOUT1           Path to output file. 
 144     #   DIRIN1             Path to input directory. 
 145     #   FILEIN1            Path to input file. 
 146     #   OPTION_FILEOUT1_OVERWRITE Indicates whether file FILEOUT1 should be overwritten (ex: "true", "false') 
 147     #   DIGEST_ALGO        Name of digest command (unverified). 
 148     # Script function dependencies: 
 149     #   - echoerr          Displays messages to stderr. 
 150     #   - vbm              Displays messsages to stderr if OPTION_VERBOSE set to "true". 
 151     # External dependencies: bash (5.0.3), echo 
 152     # Last modified: 2020-05-04T18:13Z 
 153     # Last modified by: Steven Baltakatei Sandoval 
 156     #  [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347 
 158     # Initialize function 
 159     #vbm "DEBUG:processArguments function called." 
 162     while [ ! $# -eq 0 ]; do   # While number of arguments ($#) is not (!) equal to (-eq) zero (0). 
 163         #1>&2 echo "DEBUG:Starting processArguments while loop." # Debug stderr message. See [1]. 
 164         #1>&2 echo "DEBUG:Provided arguments are:""$@" # Debug stderr message. See [1]. 
 166             -h | 
--help) showUsage
; exit 1;; # Display usage. 
 167             --version) showVersion
; exit 1;; # Show version 
 168             -v | 
--verbose) OPTION_VERBOSE
="true"; vbm 
"DEBUG:Verbose mode enabled.";; # Enable verbose mode. See [1]. 
 169             -i | 
--input-file) # Define input file path 
 170                 if [ -f "$2" ]; then # If $2 is file that exists, set FILEIN1 to $2, pop $2. 
 173                     vbm 
"DEBUG:Input file FILEIN1 set to:""$2"; 
 175                     echoerr 
"ERROR: Specified input file does not exist:""$2"; 
 179             -I | 
--input-dir) # Define input directory path. 
 180                 if [ -d "$2" ]; then # If $2 is dir that exists, set DIRIN1 to $2, pop $2. 
 183                     vbm 
"DEBUG:Input directory DIRIN1 set to:""$2"; 
 184                 else # Display error if $2 is not a valid dir. 
 185                     echoerr 
"ERROR:Specified input directory does not exist:""$2"; 
 189             -o | 
--output-file) # Define output file path 
 190                 if [ -f "$2" ]; then # If $2 is file that exists, prompt user to continue to overwrite, set FILEOUT1 to $2, pop $2. 
 191                     echoerr 
"Specified output file $2 already exists. Overwrite? (y/n):" 
 193                                 y | Y | 
yes) OPTION_FILEOUT1_OVERWRITE
="true";; 
 194                                 n | N | no
) OPTION_FILEOUT1_OVERWRITE
="false";; 
 195                                 *) echoerr 
"Invalid selection. Exiting."; exit 1;; 
 197                     if [ "$OPTION_FILEOUT1_OVERWRITE" == "true" ]; then 
 200                         vbm 
"DEBUG:Output file FILEOUT1 set to:""$2"; 
 202                         echoerr 
"ERORR:Exiting in order to not overwrite output file:""$FILEOUT1"; 
 208                     vbm 
"DEBUG:Output file FILEOUT1 set to:""$2"; 
 210             -O | 
--output-dir) # Define output directory path 
 211                 if [ -d "$2" ]; then # If $2 is dir that exists, set DIROUT1 to $2, pop $2 
 214                     vbm 
"DEBUG:Output directory DIROUT1 set to:""$2"; 
 216                     echoerr 
"ERROR:Specified output directory is not valid:""$2"; 
 220             --digest-algo) # Define file digest algorithm command name. 
 221                 if ! [ -z "$2" ]; then # If $2 is non-zero, set DIGEST_ALGO to $2, pop $2 
 224                     vbm 
"DEBUG:DIGEST_ALGO set to:""$DIGEST_ALGO"; 
 226                     echoerr 
"ERROR:Cannot set DIGEST_ALGO because no string specified."; echoerr 
"Exiting."; exit 1; 
 228             *) if [ -f "$1" ] && ! [ -v FILEIN1 
]; then # If $1 is file and FILEIN1 not set, then set FILEIN1 to $1, pop $1. 
 231                    vbm 
"DEBUG:FILEIN1 set to:""$FILEIN1"; 
 232                else # Handle non-option (ex: [FILE] ) 
 233                    echoerr 
"ERROR: Unrecognized non-option argument."; echoerr 
"Exiting."; exit 1; 
 240     vbm 
"DEBUG:processArguments function ended." 
 241     return  0; # Function finished. 
 242 } # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.). 
 244     # Usage: checkExecutables [ command1 ] [ command2 ] [...] [ commandN ] 
 245     # Description: Checks that provided commands exist. If one or more do not exist, then display missing commands and exit with error. 
 247     #   - command names (arguments) 
 248     # Output: commands that don't exist (stderr) 
 249     # Script function dependencies: 
 250     #   - echoerr          for displaying errors via stderr 
 251     #   - processArguments for setting OPTION_VERBOSE 
 252     #   - vbm              for displaying verbose messages if OPTION_VERBOSE is "true" 
 253     # External dependencies: bash (5.0.3), command 
 254     # Last modified: 2020-05-04T17:12Z 
 255     # Last modified by: Steven Baltakatei Sandoval 
 258     #  [1]: SiegeX (2010-12-12). ["Difference between return and exit in Bash functions."](https://stackoverflow.com/a/4419971). Licensed CC BY-SA 4.0. 
 259     #  [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. 
 260     #  [3]: kojiro (2012-10-03). ["Convert command line arguments into an array in Bash"](https://stackoverflow.com/a/12711853) 
 261     #  [4]: niieani (2016-01-12). ["How to copy an array in Bash?"](https://stackoverflow.com/a/34733375/10850071). Licensed CC BY-SA 4.0. 
 263     # Initialize function 
 264     vbm 
"DEBUG:checkExecutables function called." 
 265     declare -a candidateCommandsNames 
# Initialize array for storing positional arguments provided to this function. 
 266     candidateCommandsNames
=("$@") # Save positional arguments to variable as string. See [3]. 
 267     vbm 
"DEBUG:candidateCommandsNames:""$@" 
 268     vbm 
"DEBUG:candidateCommandsNames[0]:""${candidateCommandsNames[0]}" 
 269     vbm 
"DEBUG:candidateCommandsNames[1]:""${candidateCommandsNames[1]}" 
 270     vbm 
"DEBUG:candidateCommandsNames[2]:""${candidateCommandsNames[2]}" 
 271     declare -a outputInvalidCommandsArray 
# Initialize arary for storing names of invalid commands. 
 272     declare -a outputValidCommandsArray 
# Initialize array for storing names of valid commands. 
 275     for candidateCommandName 
in "${candidateCommandsNames[@]}"; do  # Search through all space-delimited text for valid commands. 
 276         if command -v "$candidateCommandName" 1>/dev
/null 
2>/dev
/null
; then # Check if a command is valid or not. 
 277             outputValidCommandsArray
+=("$candidateCommandName") ; # See [2]. 
 278             vbm 
"DEBUG:Adding $candidateCommandName to outputValidCommandsArray." 
 280             outputInvalidCommandsArray
+=("$candidateCommandName") ; # See [2]. 
 281             vbm 
"DEBUG:Adding $candidateCommandName to outputInvalidCommandsArray." 
 286     if [ ${#outputInvalidCommandsArray[@]} -gt 0 ]; then # if number of elements in outputInvalidCommandsArray greater than 0, then display offending commands and exit 1. 
 287         echoerr 
"ERROR: Invalid commands found:""${outputInvalidCommandsArray[@]}"; # display invalid commands as error 
 289     elif [ ${#outputInvalidCommandsArray[@]} -eq 0 ]; then # if number of elements in outputInvalidCommandsArray equals zero, then return 0. 
 290         vbm 
"DEBUG: Valid commands are:""${outputValidCommandsArray[@]}"; # display valid commands if verbose mode enabled 
 293         echoerr 
"ERROR: Check outputInvalidCommandsArray."; 
 297     vbm 
"DEBUG:checkExecutables function ended." 
 298     return 0; # Function finished. 
 299 } # Check that certain executables exist. 
 300 updateTimeConstants
() { 
 301     # Usage: updateTimeConstants 
 302     # Description: Updates time-related variables for use by other scripts. 
 304     # Output: Sets following variables: 
 305     #   TIME_CURRENT       Current time in long ISO-8601 format.  (ex: YYYY-mm-ddTHH:MM:SS+ZZZZ) 
 306     #   TIME_CURRENT_SHORT Current time in short ISO-8601 format. (ex: YYYYmmddTHHMMSS+ZZZZ) 
 307     #   DATE_CURRENT       Current date in ISO-8601 format.       (ex: YYYY-mm-dd) 
 308     #   DATE_CURRENT_SHORT Current date in short ISO-8601 format. (ex: YYYYmmdd) 
 309     #   DATE_TOMORROW      Tomorrow's date in ISO-8601 format.    (ex: YYYY-mm-dd) 
 310     #   TIME_NEXT_MIDNIGHT Time of tomorrow's midnight in long    (ex: YYYY-mm-ddTHH:MM:SS+ZZZZ) 
 312     #   SEC_TIL_MIDNIGHT   Seconds until next midnight. 
 313     # Script function dependencies: 
 314     #   - echoerr          for displaying errors via stderr 
 315     #   - processArguments for setting OPTION_VERBOSE 
 316     #   - vbm              for displaying verbose messages if OPTION_VERBOSE is "true" 
 317     # External dependencies: bash (5.0.3), date, echo 
 318     # Last modified: 2020-04-11T23:59Z 
 319     # Last modified by: Steven Baltakatei Sandoval 
 323     # Initialize function 
 324     vbm 
"DEBUG:updateTimeConstants function called." 
 327     TIME_CURRENT
="$(date --iso-8601=seconds)" ; 
 328     TIME_CURRENT_SHORT
="$(date -d "$TIME_CURRENT" +%Y%m%dT%H%M%S%z)" 
 329     DATE_CURRENT
="$(date -d "$TIME_CURRENT" --iso-8601=date)" ; 
 330     DATE_CURRENT_SHORT
="$(date -d "$TIME_CURRENT" +%Y%m%d)" ; 
 331     DATE_TOMORROW
="$(date -d "$TIME_CURRENT next day
" --iso-8601=date)" ; 
 332     TIME_NEXT_MIDNIGHT
="$(date -d "$DATE_TOMORROW" --iso-8601=seconds)" ; 
 333     SECONDS_UNTIL_NEXT_MIDNIGHT
="$((  $(date +%s -d "$TIME_NEXT_MIDNIGHT") - $(date +%s -d "$TIME_CURRENT")  ))" ; 
 336     vbm 
"DEBUG:updateTimeConstants function ended." 
 337     return 0; # Function finished. 
 338 } # Update time constants 
 340     # Usage: getGnucuDigest [algo] [file] 
 341     # Description: Calculates cryptographic digest using specified GnuCoreutils command. 
 343     #   $1: [algo]     (ex: sha256sum, sha512sum, b2sum, md5sum) 
 346     # Script function dependencies: 
 347     #   - echoerr          for displaying errors via stderr 
 348     #   - processArguments for setting OPTION_VERBOSE 
 349     #   - vbm              for displaying verbose messages if OPTION_VERBOSE is "true" 
 350     #   - checkExecutables for checking that digest algo commands exist 
 351     # External dependencies: bash (5.0.3), echo, awk 
 352     # Last modified: 2020-05-04T18:51Z 
 353     # Last modified by: Steven Baltakatei Sandoval 
 356     #  [1]: gnu.org (2000-11). [GNU Coreutils](https://www.gnu.org/software/coreutils/manual/coreutils.html). Licensed FDL v1.3+. 
 357     #  [2]: linuxize.com (2019-07-19). [How to Check if a String Contains a Substring in Bash](https://linuxize.com/post/how-to-check-if-string-contains-substring-in-bash/). 
 359     # Initialize function 
 360     vbm 
"DEBUG:getGnucuDigest function called." 
 361     DIGEST_ALGO
=$1 # Define candidate digest algorithm 
 362     TARGET_FILE
=$2 # Define target file to be digested 
 363     GNUCU_DIGESTS
="b2sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum" # Define space-delimited string with GNU Coreutils digest algorithms. See [1]. 
 364     vbm 
"DEBUG:DIGEST_ALGO set to:""$DIGEST_ALGO" 
 365     vbm 
"TARGET_FILE set to:""$TARGET_FILE" 
 366     vbm 
"GNUCU_DIGESTS set to:""$GNUCU_DIGESTS" 
 367     checkExecutables 
"$DIGEST_ALGO"; # Check that specified alogirthm command available. 
 368     if [[ "$GNUCU_DIGESTS" =~ .
*"$DIGEST_ALGO".
* ]]; then 
 369         vbm 
"DEBUG:Confirmed to be a valid GNU Coreutils command:""$DIGEST_ALGO"; 
 371         vbm 
"ERROR:Not a valid GNU coreutils command:""$DIGEST_ALGO"; 
 373     fi # Check specified algo against GNUCU_DIGESTS. See [2]. 
 376     OUTPUT
="$("$DIGEST_ALGO" "$TARGET_FILE" | awk '{print $1}')" 
 377     vbm 
"DEBUG:Calculated digest using $DIGEST_ALGO is:""$OUTPUT" 
 383     vbm 
"DEBUG:getGnucuDigest function ended." 
 384     return 0; # Function finished. 
 385 } # Calculate GNUCoreutils digest 
 388 #== Main Program Definition == 
 390     # Usage: main "$@"    # See [1]. 
 391     # Input: unspecified (note: all Global Constants declared at beginning of script assumed to be available) 
 392     #   OPTION_VERBOSE   (used by vbm, set by processArguments)  (ex: "true", "false") 
 393     #   DIGEST_ALGO      (used by getGnucuDigest, set by processArguments) (ex: "md5sum") 
 394     # Output: unspecified 
 395     # Script function dependencies: 
 396     #   - echoerr          for displaying errors via stderr 
 397     #   - vbm              for displaying verbose messages if OPTION_VERBOSE is "true" 
 398     #   - processArguments for setting OPTION_VERBOSE 
 399     #   - checkExecutables for checking that specified commands are available 
 400     # External dependencies: bash (5.0.3), echo 
 401     # Last modified: 2020-05-04T18:44Z 
 402     # Last modified by: Steven Baltakatei Sandoval 
 405     #  [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. 
 407     # Initialize function 
 408     processArguments 
"$@" # Process arguments. 
 409     vbm 
"DEBUG:main function called." 
 410     vbm 
"DEBUG:main function arguments are:""$@" 
 411     checkExecutables 
"echo" "date" "cut" "awk" "grep" # Confirm that executables necessary to run are available. Exit if any fail. 
 414     # If DIRIN1 unset and global variable BK_FILEHASHLIST_DIR set and is dir, then set DIRIN1 to BK_FILEHASHLIST_DIR. 
 415     if ! [ -v DIRN1 
] && [ -v BK_FILEHASHLIST_DIR 
] && [ -d "$BK_FILEHASHLIST_DIR" ]; then 
 416         DIRIN1
="$BK_FILEHASHLIST_DIR"; 
 417         vbm 
"DEBUG:DIRN1 not set, BK_FILEHASHLIST_DIR is dir, so setting DIRIN1 to BK_FILEHASHLIST_DIR:""$BK_FILEHASHLIST_DIR"; 
 421     if ! [ -v DIGEST_ALGO 
]; then # If DIGEST_ALGO is not set then set it to default value of "b2sum" 
 423         vbm 
"DEBUG:DIGEST_ALGO not set. Setting to default value:""$DIGEST_ALGO" 
 427     # if [ -v FILEIN1 ]; then # VERBOSE: Display contents of FILEIN1 if FILEIN1 has been set. 
 428     #   vbm "DEBUG:main function detects input file:""$FILEIN1" ; 
 429     #   vbm "DEBUG:contents of following file is displayed below:""$FILEIN1" ; 
 430     #   if [ "$OPTION_VERBOSE" == "true" ]; then # display FILEIN1 contents via cat if verbose mode specified 
 431     #       echoerr "========""$FILEIN1"" START""========" ; 
 432     #       cat "$FILEIN1" 1>&2 ; 
 433     #       echoerr "========""$FILEIN1"" END""========" ; 
 439     DIGEST
="$(getGnucuDigest "$DIGEST_ALGO" "$FILEIN1")" # Calculate digest of FILEIN1 
 440     vbm 
"DEBUG:Hash of $FILEIN1 is:""$DIGEST" 
 441     vbm 
"DEBUG:Value of DIGEST is:""$DIGEST" 
 442     vbm 
"DEBUG:Value of DIRIN1 is:""$DIRIN1" 
 443     OUTPUT
="$(grep -ri "$DIGEST" "$DIRIN1")" # Search contents of DIRIN1 for pattern DIGEST, save results to OUTPUT. 
 448     if [ -v FILEOUT1 
]; then  # if FILEOUT1 set, write to this file. 
 449         vbm 
"DEBUG:main function detects FILEOUT1 set to:""$FILEOUT1" ; 
 450         echo -e "$OUTPUT" > "$FILEOUT1" ; 
 451         vbm 
"DEBUG:main funtion wrote OUTPUT to:""$FILEOUT1" ; 
 452     elif [ -v DIROUT1 
]; then  # else if DIROUT1 set, set FILEOUT1 to timestamped filename, combine into PATHOUT1, write output to PATHOUT1. 
 453         vbm 
"DEBUG:main function detects DIROUT1 set to:""$DIROUT1" ; 
 454         FILEOUT1
="$SCRIPT_TIME_SHORT"..
"$(basename "$0")".txt
 
 455         PATHOUT1
="$DIROUT1"/"$FILEOUT1" 
 456         echo -e "$OUTPUT" > $PATHOUT1 ; 
 457         vbm 
"DEBUG:main function wrote output file to:""$PATHOUT1" ; 
 461     vbm 
"DEBUG:main function ended." 
 462     return 0; # Function finished. 
 465 #== Perform work and exit == 
 466 main 
"$@" # Run main function.