--- /dev/null
+#!/bin/bash
+
+# Date: 2020-05-04T18:50Z
+# Author: Steven Baltakatei Sandoval
+# Description: Script to lookup file hash from existing hash list.
+# Note: Use hide-show-block function to aid readability. (ex: https://www.emacswiki.org/emacs/HideShow ).
+
+#== Variable Initialization ==
+
+#== Global constants ==
+SCRIPT_TTL=10 # Limit script life to this in seconds.
+PATH="/usr/local/bin/:$PATH" # Add user binary executable directory to PATH.
+PATH="/opt/bktei:$PATH" # Add 'optional' executable directory to PATH.
+SCRIPT_HOSTNAME=$(hostname) # Save hostname of system running this script.
+SCRIPT_VERSION="bktemplate.sh 0.0.0" # Define version of script. Used by function 'showVersion'.
+SCRIPT_TIME_SHORT="$(date +%Y%m%dT%H%M%S%z)" # Save current date & time in ISO-8601 format (YYYYmmddTHHMMSS+zzzz).
+SCRIPT_DATE_SHORT="$(date +%Y%m%d)" # Save current date in ISO-8601 format.
+
+#== Function Definitions ==
+echoerr() {
+ # Usage: echo [ arguments ]
+ # Description: Prints provided arguments to stderr.
+ # Input: unspecified
+ # Output: stderr
+ # Script function dependencies: none
+ # External function dependencies: echo
+ # Last modified: 2020-04-11T21:16Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib:
+ # [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.
+
+ echo "$@" 1>&2; # Define stderr echo function. See [1].
+ return 0; # Function finished.
+} # Define stderr message function.
+vbm() {
+ # Usage: vbm "DEBUG:verbose message here"
+ # Description: Prints verbose message ("vbm") to stderr if OPTION_VERBOSE is set to "true".
+ # Input:
+ # - OPTION_VERBOSE variable set by processArguments function. (ex: "true", "false")
+ # - "$@" positional arguments fed to this function.
+ # Output: stderr
+ # Script function dependencies: echoerr
+ # External function dependencies: echo
+ # Last modified: 2020-04-11T23:57Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib:
+
+ if [ "$OPTION_VERBOSE" == "true" ]; then
+ FUNCTION_TIME=$(date --iso-8601=ns); # Save current time in nano seconds.
+ echoerr "[$FUNCTION_TIME] ""$@"; # Display argument text.
+ fi
+
+ # End function
+ return 0; # Function finished.
+} # Verbose message display function.
+showUsage() {
+ # Usage: showUsage
+ # Description: Displays script usage information.
+ # Input: none
+ # Output: stderr
+ # Script function dependencies: echoerr
+ # External dependencies: bash (5.0.3), echo
+ # Last modified: 2020-05-04T16:58Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib.:
+
+ echoerr "USAGE:"
+ echoerr " bklu [OPTIONS] [FILE]"
+ echoerr
+ echoerr " [FILE] hash is compared against file contents of directory"
+ echoerr " specified by --input-dir option or environment variable"
+ echoerr " BK_FILEHASHLIST_DIR ."
+ echoerr
+ echoerr "OPTIONS:"
+ echoerr " --digest-algo [ digest name ]"
+ echoerr " Specify digest command name. Value should be GNU"
+ echoerr " coreutils commands such as: sha256sum, md5sum, b2sum."
+ echoerr " Default value is: b2sum"
+ echoerr
+ echoerr " -h, --help"
+ echoerr " Display help information."
+ echoerr
+ echoerr " --version"
+ echoerr " Display script version."
+ echoerr
+ echoerr " -v, --verbose"
+ echoerr " Display debugging info."
+ echoerr
+ echoerr " -o, --output-file [ file ]"
+ echoerr " Specify output file."
+ echoerr
+ echoerr " -i, --input-file [ file ]"
+ echoerr " Specify input file."
+ echoerr
+ echoerr " -o, --output-dir [ directory ]"
+ echoerr " Specify output directory."
+ echoerr
+ echoerr " -i, --input-dir [ directory ]"
+ echoerr " Specify input directory."
+ echoerr
+
+
+ # End function
+ return 0; # Function finished.
+} # Display information on how to use this script.
+showVersion() {
+ # Usage: showVersion
+ # Descriptoin: Displays script version and license information.
+ # Input: unspecified
+ # Output: stderr
+ # Script function dependencies: echoerr
+ # External function dependencies: echo
+ # Last modified: 2020-04-11T23:57Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib:
+
+ # Initialize function
+ vbm "DEBUG:showVersion function called."
+
+ # Perform work
+ OUTPUT="$SCRIPT_VERSION"
+
+ # Display results
+ echoerr "$OUTPUT";
+
+ # End function
+ vbm "DEBUG:showVersion function ended."
+ return 0; # Function finished.
+} # Display script version.
+processArguments() {
+ # Usage: processArguments "$@"
+ # Description: Processes provided arguments in order to set script option variables useful for
+ # changing how other functions behave. For example, it may:
+ # 1. Activate verbose mode
+ # Input: "$@" (list of arguments provided to the function)
+ # Output: Sets following variables used by other functions:
+ # OPTION_VERBOSE Indicates verbose mode enable status. (ex: "true", "false")
+ # DIROUT1 Path to output directory.
+ # FILEOUT1 Path to output file.
+ # DIRIN1 Path to input directory.
+ # FILEIN1 Path to input file.
+ # OPTION_FILEOUT1_OVERWRITE Indicates whether file FILEOUT1 should be overwritten (ex: "true", "false')
+ # DIGEST_ALGO Name of digest command (unverified).
+ # Script function dependencies:
+ # - echoerr Displays messages to stderr.
+ # - vbm Displays messsages to stderr if OPTION_VERBOSE set to "true".
+ # External dependencies: bash (5.0.3), echo
+ # Last modified: 2020-05-04T18:13Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib.:
+ # [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347
+
+ # Initialize function
+ #vbm "DEBUG:processArguments function called."
+
+ # Perform work
+ while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0).
+ #1>&2 echo "DEBUG:Starting processArguments while loop." # Debug stderr message. See [1].
+ #1>&2 echo "DEBUG:Provided arguments are:""$@" # Debug stderr message. See [1].
+ case "$1" in
+ -h | --help) showUsage; exit 1;; # Display usage.
+ --version) showVersion; exit 1;; # Show version
+ -v | --verbose) OPTION_VERBOSE="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode. See [1].
+ -i | --input-file) # Define input file path
+ if [ -f "$2" ]; then # If $2 is file that exists, set FILEIN1 to $2, pop $2.
+ FILEIN1="$2";
+ shift;
+ vbm "DEBUG:Input file FILEIN1 set to:""$2";
+ else
+ echoerr "ERROR: Specified input file does not exist:""$2";
+ echoerr "Exiting.";
+ exit 1;
+ fi ;;
+ -I | --input-dir) # Define input directory path.
+ if [ -d "$2" ]; then # If $2 is dir that exists, set DIRIN1 to $2, pop $2.
+ DIRIN1="$2";
+ shift;
+ vbm "DEBUG:Input directory DIRIN1 set to:""$2";
+ else # Display error if $2 is not a valid dir.
+ echoerr "ERROR:Specified input directory does not exist:""$2";
+ echoerr "Exiting.";
+ exit 1;
+ fi ;;
+ -o | --output-file) # Define output file path
+ if [ -f "$2" ]; then # If $2 is file that exists, prompt user to continue to overwrite, set FILEOUT1 to $2, pop $2.
+ echoerr "Specified output file $2 already exists. Overwrite? (y/n):"
+ read m; case $m in
+ y | Y | yes) OPTION_FILEOUT1_OVERWRITE="true";;
+ n | N | no) OPTION_FILEOUT1_OVERWRITE="false";;
+ *) echoerr "Invalid selection. Exiting."; exit 1;;
+ esac
+ if [ "$OPTION_FILEOUT1_OVERWRITE" == "true" ]; then
+ FILEOUT1="$2";
+ shift;
+ vbm "DEBUG:Output file FILEOUT1 set to:""$2";
+ else
+ echoerr "ERORR:Exiting in order to not overwrite output file:""$FILEOUT1";
+ exit 1;
+ fi
+ else
+ FILEOUT1="$2";
+ shift;
+ vbm "DEBUG:Output file FILEOUT1 set to:""$2";
+ fi ;;
+ -O | --output-dir) # Define output directory path
+ if [ -d "$2" ]; then # If $2 is dir that exists, set DIROUT1 to $2, pop $2
+ DIROUT1="$2";
+ shift;
+ vbm "DEBUG:Output directory DIROUT1 set to:""$2";
+ else
+ echoerr "ERROR:Specified output directory is not valid:""$2";
+ echoerr "Exiting.";
+ exit 1;
+ fi ;;
+ --digest-algo) # Define file digest algorithm command name.
+ if ! [ -z "$2" ]; then # If $2 is non-zero, set DIGEST_ALGO to $2, pop $2
+ DIGEST_ALGO="$2";
+ shift;
+ vbm "DEBUG:DIGEST_ALGO set to:""$DIGEST_ALGO";
+ else
+ echoerr "ERROR:Cannot set DIGEST_ALGO because no string specified."; echoerr "Exiting."; exit 1;
+ fi ;;
+ *) if [ -f "$1" ] && ! [ -v FILEIN1 ]; then # If $1 is file and FILEIN1 not set, then set FILEIN1 to $1, pop $1.
+ FILEIN1="$1";
+ shift;
+ vbm "DEBUG:FILEIN1 set to:""$FILEIN1";
+ else # Handle non-option (ex: [FILE] )
+ echoerr "ERROR: Unrecognized non-option argument."; echoerr "Exiting."; exit 1;
+ fi ;;
+ esac
+ shift;
+ done
+
+ # End function
+ vbm "DEBUG:processArguments function ended."
+ return 0; # Function finished.
+} # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.).
+checkExecutables() {
+ # Usage: checkExecutables [ command1 ] [ command2 ] [...] [ commandN ]
+ # Description: Checks that provided commands exist. If one or more do not exist, then display missing commands and exit with error.
+ # Input:
+ # - command names (arguments)
+ # Output: commands that don't exist (stderr)
+ # Script function dependencies:
+ # - echoerr for displaying errors via stderr
+ # - processArguments for setting OPTION_VERBOSE
+ # - vbm for displaying verbose messages if OPTION_VERBOSE is "true"
+ # External dependencies: bash (5.0.3), command
+ # Last modified: 2020-05-04T17:12Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib.:
+ # [1]: SiegeX (2010-12-12). ["Difference between return and exit in Bash functions."](https://stackoverflow.com/a/4419971). Licensed CC BY-SA 4.0.
+ # [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.
+ # [3]: kojiro (2012-10-03). ["Convert command line arguments into an array in Bash"](https://stackoverflow.com/a/12711853)
+ # [4]: niieani (2016-01-12). ["How to copy an array in Bash?"](https://stackoverflow.com/a/34733375/10850071). Licensed CC BY-SA 4.0.
+
+ # Initialize function
+ vbm "DEBUG:checkExecutables function called."
+ declare -a candidateCommandsNames # Initialize array for storing positional arguments provided to this function.
+ candidateCommandsNames=("$@") # Save positional arguments to variable as string. See [3].
+ vbm "DEBUG:candidateCommandsNames:""$@"
+ vbm "DEBUG:candidateCommandsNames[0]:""${candidateCommandsNames[0]}"
+ vbm "DEBUG:candidateCommandsNames[1]:""${candidateCommandsNames[1]}"
+ vbm "DEBUG:candidateCommandsNames[2]:""${candidateCommandsNames[2]}"
+ declare -a outputInvalidCommandsArray # Initialize arary for storing names of invalid commands.
+ declare -a outputValidCommandsArray # Initialize array for storing names of valid commands.
+
+ # Perform work
+ for candidateCommandName in "${candidateCommandsNames[@]}"; do # Search through all space-delimited text for valid commands.
+ if command -v "$candidateCommandName" 1>/dev/null 2>/dev/null; then # Check if a command is valid or not.
+ outputValidCommandsArray+=("$candidateCommandName") ; # See [2].
+ vbm "DEBUG:Adding $candidateCommandName to outputValidCommandsArray."
+ else
+ outputInvalidCommandsArray+=("$candidateCommandName") ; # See [2].
+ vbm "DEBUG:Adding $candidateCommandName to outputInvalidCommandsArray."
+ fi
+ done
+
+ # Output results
+ if [ ${#outputInvalidCommandsArray[@]} -gt 0 ]; then # if number of elements in outputInvalidCommandsArray greater than 0, then display offending commands and exit 1.
+ echoerr "ERROR: Invalid commands found:""${outputInvalidCommandsArray[@]}"; # display invalid commands as error
+ exit 1; # See [1].
+ elif [ ${#outputInvalidCommandsArray[@]} -eq 0 ]; then # if number of elements in outputInvalidCommandsArray equals zero, then return 0.
+ vbm "DEBUG: Valid commands are:""${outputValidCommandsArray[@]}"; # display valid commands if verbose mode enabled
+ return 0; # See [1].
+ else
+ echoerr "ERROR: Check outputInvalidCommandsArray.";
+ fi
+
+ # End function
+ vbm "DEBUG:checkExecutables function ended."
+ return 0; # Function finished.
+} # Check that certain executables exist.
+updateTimeConstants() {
+ # Usage: updateTimeConstants
+ # Description: Updates time-related variables for use by other scripts.
+ # Input: (none)
+ # Output: Sets following variables:
+ # TIME_CURRENT Current time in long ISO-8601 format. (ex: YYYY-mm-ddTHH:MM:SS+ZZZZ)
+ # TIME_CURRENT_SHORT Current time in short ISO-8601 format. (ex: YYYYmmddTHHMMSS+ZZZZ)
+ # DATE_CURRENT Current date in ISO-8601 format. (ex: YYYY-mm-dd)
+ # DATE_CURRENT_SHORT Current date in short ISO-8601 format. (ex: YYYYmmdd)
+ # DATE_TOMORROW Tomorrow's date in ISO-8601 format. (ex: YYYY-mm-dd)
+ # TIME_NEXT_MIDNIGHT Time of tomorrow's midnight in long (ex: YYYY-mm-ddTHH:MM:SS+ZZZZ)
+ # ISO-861 format.
+ # SEC_TIL_MIDNIGHT Seconds until next midnight.
+ # Script function dependencies:
+ # - echoerr for displaying errors via stderr
+ # - processArguments for setting OPTION_VERBOSE
+ # - vbm for displaying verbose messages if OPTION_VERBOSE is "true"
+ # External dependencies: bash (5.0.3), date, echo
+ # Last modified: 2020-04-11T23:59Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib.:
+
+ # Initialize function
+ vbm "DEBUG:updateTimeConstants function called."
+
+ # Perform work
+ TIME_CURRENT="$(date --iso-8601=seconds)" ;
+ TIME_CURRENT_SHORT="$(date -d "$TIME_CURRENT" +%Y%m%dT%H%M%S%z)"
+ DATE_CURRENT="$(date -d "$TIME_CURRENT" --iso-8601=date)" ;
+ DATE_CURRENT_SHORT="$(date -d "$TIME_CURRENT" +%Y%m%d)" ;
+ DATE_TOMORROW="$(date -d "$TIME_CURRENT next day" --iso-8601=date)" ;
+ TIME_NEXT_MIDNIGHT="$(date -d "$DATE_TOMORROW" --iso-8601=seconds)" ;
+ SECONDS_UNTIL_NEXT_MIDNIGHT="$(( $(date +%s -d "$TIME_NEXT_MIDNIGHT") - $(date +%s -d "$TIME_CURRENT") ))" ;
+
+ # End function
+ vbm "DEBUG:updateTimeConstants function ended."
+ return 0; # Function finished.
+} # Update time constants
+getGnucuDigest() {
+ # Usage: getGnucuDigest [algo] [file]
+ # Description: Calculates cryptographic digest using specified GnuCoreutils command.
+ # Input:
+ # $1: [algo] (ex: sha256sum, sha512sum, b2sum, md5sum)
+ # $2: [file]
+ # Output: stdout
+ # Script function dependencies:
+ # - echoerr for displaying errors via stderr
+ # - processArguments for setting OPTION_VERBOSE
+ # - vbm for displaying verbose messages if OPTION_VERBOSE is "true"
+ # - checkExecutables for checking that digest algo commands exist
+ # External dependencies: bash (5.0.3), echo, awk
+ # Last modified: 2020-05-04T18:51Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib.:
+ # [1]: gnu.org (2000-11). [GNU Coreutils](https://www.gnu.org/software/coreutils/manual/coreutils.html). Licensed FDL v1.3+.
+ # [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/).
+
+ # Initialize function
+ vbm "DEBUG:getGnucuDigest function called."
+ DIGEST_ALGO=$1 # Define candidate digest algorithm
+ TARGET_FILE=$2 # Define target file to be digested
+ GNUCU_DIGESTS="b2sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum" # Define space-delimited string with GNU Coreutils digest algorithms. See [1].
+ vbm "DEBUG:DIGEST_ALGO set to:""$DIGEST_ALGO"
+ vbm "TARGET_FILE set to:""$TARGET_FILE"
+ vbm "GNUCU_DIGESTS set to:""$GNUCU_DIGESTS"
+ checkExecutables "$DIGEST_ALGO"; # Check that specified alogirthm command available.
+ if [[ "$GNUCU_DIGESTS" =~ .*"$DIGEST_ALGO".* ]]; then
+ vbm "DEBUG:Confirmed to be a valid GNU Coreutils command:""$DIGEST_ALGO";
+ else
+ vbm "ERROR:Not a valid GNU coreutils command:""$DIGEST_ALGO";
+ exit 1;
+ fi # Check specified algo against GNUCU_DIGESTS. See [2].
+
+ # Perform work
+ OUTPUT="$("$DIGEST_ALGO" "$TARGET_FILE" | awk '{print $1}')"
+ vbm "DEBUG:Calculated digest using $DIGEST_ALGO is:""$OUTPUT"
+
+ # Output results
+ echo "$OUTPUT"
+
+ # End function
+ vbm "DEBUG:getGnucuDigest function ended."
+ return 0; # Function finished.
+} # Calculate GNUCoreutils digest
+
+
+#== Main Program Definition ==
+main() {
+ # Usage: main "$@" # See [1].
+ # Input: unspecified (note: all Global Constants declared at beginning of script assumed to be available)
+ # OPTION_VERBOSE (used by vbm, set by processArguments) (ex: "true", "false")
+ # DIGEST_ALGO (used by getGnucuDigest, set by processArguments) (ex: "md5sum")
+ # Output: unspecified
+ # Script function dependencies:
+ # - echoerr for displaying errors via stderr
+ # - vbm for displaying verbose messages if OPTION_VERBOSE is "true"
+ # - processArguments for setting OPTION_VERBOSE
+ # - checkExecutables for checking that specified commands are available
+ # External dependencies: bash (5.0.3), echo
+ # Last modified: 2020-05-04T18:44Z
+ # Last modified by: Steven Baltakatei Sandoval
+ # License: GPLv3+
+ # Ref./Attrib.:
+ # [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.
+
+ # Initialize function
+ processArguments "$@" # Process arguments.
+ vbm "DEBUG:main function called."
+ vbm "DEBUG:main function arguments are:""$@"
+ checkExecutables "echo" "date" "cut" "awk" "grep" # Confirm that executables necessary to run are available. Exit if any fail.
+
+ # Process inputs
+ # If DIRIN1 unset and global variable BK_FILEHASHLIST_DIR set and is dir, then set DIRIN1 to BK_FILEHASHLIST_DIR.
+ if ! [ -v DIRN1 ] && [ -v BK_FILEHASHLIST_DIR ] && [ -d "$BK_FILEHASHLIST_DIR" ]; then
+ DIRIN1="$BK_FILEHASHLIST_DIR";
+ vbm "DEBUG:DIRN1 not set, BK_FILEHASHLIST_DIR is dir, so setting DIRIN1 to BK_FILEHASHLIST_DIR:""$BK_FILEHASHLIST_DIR";
+ fi
+
+ # Set defaults
+ if ! [ -v DIGEST_ALGO ]; then # If DIGEST_ALGO is not set then set it to default value of "b2sum"
+ DIGEST_ALGO="b2sum";
+ vbm "DEBUG:DIGEST_ALGO not set. Setting to default value:""$DIGEST_ALGO"
+ fi
+
+
+ # if [ -v FILEIN1 ]; then # VERBOSE: Display contents of FILEIN1 if FILEIN1 has been set.
+ # vbm "DEBUG:main function detects input file:""$FILEIN1" ;
+ # vbm "DEBUG:contents of following file is displayed below:""$FILEIN1" ;
+ # if [ "$OPTION_VERBOSE" == "true" ]; then # display FILEIN1 contents via cat if verbose mode specified
+ # echoerr "========""$FILEIN1"" START""========" ;
+ # cat "$FILEIN1" 1>&2 ;
+ # echoerr "========""$FILEIN1"" END""========" ;
+ # fi
+ # fi
+
+
+ # Perform work
+ DIGEST="$(getGnucuDigest "$DIGEST_ALGO" "$FILEIN1")" # Calculate digest of FILEIN1
+ vbm "DEBUG:Hash of $FILEIN1 is:""$DIGEST"
+ vbm "DEBUG:Value of DIGEST is:""$DIGEST"
+ vbm "DEBUG:Value of DIRIN1 is:""$DIRIN1"
+ OUTPUT="$(grep -ri "$DIGEST" "$DIRIN1")" # Search contents of DIRIN1 for pattern DIGEST, save results to OUTPUT.
+
+ # Output results
+ echo -e "$OUTPUT"
+
+ if [ -v FILEOUT1 ]; then # if FILEOUT1 set, write to this file.
+ vbm "DEBUG:main function detects FILEOUT1 set to:""$FILEOUT1" ;
+ echo -e "$OUTPUT" > "$FILEOUT1" ;
+ vbm "DEBUG:main funtion wrote OUTPUT to:""$FILEOUT1" ;
+ elif [ -v DIROUT1 ]; then # else if DIROUT1 set, set FILEOUT1 to timestamped filename, combine into PATHOUT1, write output to PATHOUT1.
+ vbm "DEBUG:main function detects DIROUT1 set to:""$DIROUT1" ;
+ FILEOUT1="$SCRIPT_TIME_SHORT".."$(basename "$0")".txt
+ PATHOUT1="$DIROUT1"/"$FILEOUT1"
+ echo -e "$OUTPUT" > $PATHOUT1 ;
+ vbm "DEBUG:main function wrote output file to:""$PATHOUT1" ;
+ fi
+
+ # End function
+ vbm "DEBUG:main function ended."
+ return 0; # Function finished.
+}
+
+#== Perform work and exit ==
+main "$@" # Run main function.
+exit 0;
--- /dev/null
+#!/bin/bash
+
+# Date: 2020-01-20T16:34Z
+#
+# Author: Steven Baltakatei Sandoval (baltakatei.com)
+#
+# License: This bash script, `bknpass`, is licensed under GPLv3 or
+# later by Steven Baltakatei Sandoval:
+#
+# `bknpass`, an alphanumeric password generator
+# Copyright (C) 2020 Steven Baltakatei Sandoval (baltakatei.com)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# A copy of the GNU General Public License may be found at
+# <https://www.gnu.org/licenses/>.
+#
+# Description: This bash script generates alphanumeric passphrases
+# with a char-count determined by a user-provided number of bits of
+# entropy. The passphrase is then outputted to stdout with a trailing
+# newline. It works as follows:
+#
+# - Prompt user for an integer. This integer is the number of bits
+# of entropy that the generated password should have.
+#
+# - Check if user-provided string is an integer using `bash` regular
+# expression test.
+#
+# - Calculate the minimum number of alphanumeric characters required
+# to encode the specified number of bits of entropy.
+#
+# - This step uses `bc` to calculate a logarithm float string
+# and `awk` to convert the float into an integer, rounding up.
+#
+# - Use `tr`, `/dev/urandom`, and `head` to generate a random
+# alphanumeric string with the length calculated in the previous
+# step.
+#
+# - Use `echo` to display the passphrase in stdout with a trailing
+# newline.
+#
+# Dependencies: bash, echo, bc, awk, tr, head. See end of file
+#
+# Tested on:
+#
+# - GNU/Linux Debian 10
+
+
+#==Initialization==
+
+let ALPHABET_SIZE="26+26+10" # number of unique chars in [:alnum:], argument fed to `tr -c` in 'Generate passphrase' step)
+LOG_BASE=2 # Set logarithm base to 2
+
+# Define `echoerr` function which outputs text to stderr
+ # Note: function copied from https://stackoverflow.com/a/2990533
+function echoerr {
+ echo "$@" 1>&2;
+}
+
+# Define `rpass` function which generates an alphanumeric passphrase of length $1 (ex: `rpass 22` generates a 22-char string)
+ # Note: function adapted from https://www.thegeekstuff.com/2010/04/unix-bash-function-examples/
+function rpass {
+ cat /dev/urandom | LC_ALL=C tr -cd "[:alnum:]" | head -c ${1:-22}
+}
+
+
+#==Main Program==
+
+# Define $ENTROPY_BIT_COUNT1 as argument $1 or prompt user if $1 is not defined.
+ # note: argument test adapted from https://stackoverflow.com/a/6482403
+if [ -z "$1" ]
+then
+ echo "Entropy bit count argument (\$1) not supplied."
+ # Get from user the number of bits of entropy.
+ echoerr -n "Please specify the required strength of the password in bits of entropy (ex: 256):" # prompt via stderr
+ read ENTROPY_BIT_COUNT1
+else
+ ENTROPY_BIT_COUNT1="$1"
+fi
+
+# Check if $ENTROPY_BIT_COUNT1 is an non-negative integer
+ # Note: Regular expression test is adapted from https://stackoverflow.com/a/806923
+RETEST1='^[0-9]+$'
+if ! [[ $ENTROPY_BIT_COUNT1 =~ $RETEST1 ]] ; then
+ echo "error: Not an integer." >&2; exit 1
+fi
+
+# Calculate minimum count of chars needed to encode $ENTROPY_BIT_COUNT1 with alphabet size of $ALPHABET_SIZE as float
+ # Solve ln(a^n)/ln(2)=b for n using `bc` where
+ # a=$ALPHABET_SIZE
+ # n=$CHAR_COUNT1_FLOAT
+ # b=$ENTROPY_BIT_COUNT1
+ # Note: `bc` logarithm usage adapted from http://phodd.net/gnu-bc/bcfaq.html#bashlog
+CHAR_COUNT1_FLOAT=$(echo "$ENTROPY_BIT_COUNT1*l($LOG_BASE)/l($ALPHABET_SIZE)" | bc -l)
+ # Note: Float will be of form "21.49744370650136860806". This particular float should be rounded to "22" later.
+
+# Round $CHAR_COUNT1_FLOAT1 up to next highest integer for use as argument in later bash functions.
+ # Note: awk expression from https://bits.mdminhazulhaque.io/linux/round-number-in-bash-script.html
+CHAR_COUNT1=$(echo "$CHAR_COUNT1_FLOAT" | awk '{print ($0-int($0)>0)?int($0)+1:int($0)}')
+
+# Generate passphrase
+PASS1=$(rpass "$CHAR_COUNT1")
+echo -e "$PASS1"
+
+
+#==References==
+#
+# - How to echo a string as stderr instead of stdout.
+# https://stackoverflow.com/a/2990533
+# Author: James Roth
+# Date: 2010-06-07T14:52Z
+# Date Accessed: 2020-01-20
+#
+# - How to check if script argument exists or not.
+# https://stackoverflow.com/a/6482403
+# Author: phoxix
+# Date: 2011-06-26T05:55Z
+# Date Accessed: 2020-01-20
+#
+# - How to check that a string is an integer using regular expression test.
+# https://stackoverflow.com/a/806923
+# Author: Charles Duffy
+# Date: 2009-04-30T13:32Z
+# Date Accessed: 2020-01-20
+#
+# - How to use `bc` to calculate logarithms in Bash
+# http://phodd.net/gnu-bc/bcfaq.html#bashlog
+# Author: unknown
+# Date Accessed: 2020-01-20
+#
+# - How to use `awk` to convert and round up a float to an integer.
+# https://bits.mdminhazulhaque.io/linux/round-number-in-bash-script.html
+# Author: Md. Minhazul Haque
+# Date: 2015-01-09
+# Date Accessed: 2020-01-20
+#
+# - How to use `/dev/urandom`, `tr`, and `head` to generate a random password in Bash.
+# https://www.thegeekstuff.com/2010/04/unix-bash-function-examples/
+# Author: SASIKALA, Ramesh Natarajan
+# Date: 2010-04-21
+# Date Accessed: 2020-01-20
+#
+# - Dependencies: bash, echo, bc, awk, tr, head.
+#
+# - GNU bash, version 5.0.3(1)-release (x86_64-pc-linux-gnu)
+# Copyright (C) 2019 Free Software Foundation, Inc.
+# License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+# This is free software; you are free to change and redistribute it.
+# There is NO WARRANTY, to the extent permitted by law.
+#
+# - echo (GNU coreutils) 8.30
+# Copyright (C) 2018 Free Software Foundation, Inc.
+# License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
+# This is free software: you are free to change and redistribute it.
+# There is NO WARRANTY, to the extent permitted by law.
+#
+# Written by Brian Fox and Chet Ramey.
+#
+# - bc 1.07.1
+# Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.
+#
+# - GNU Awk 4.2.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)
+# Copyright (C) 1989, 1991-2018 Free Software Foundation.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see http://www.gnu.org/licenses/.
+#
+# - tr (GNU coreutils) 8.30
+# Copyright (C) 2018 Free Software Foundation, Inc.
+# License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
+# This is free software: you are free to change and redistribute it.
+# There is NO WARRANTY, to the extent permitted by law.
+#
+# Written by Jim Meyering.
+#
+# - head (GNU coreutils) 8.30
+# Copyright (C) 2018 Free Software Foundation, Inc.
+# License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
+# This is free software: you are free to change and redistribute it.
+# There is NO WARRANTY, to the extent permitted by law.
+#
+# Written by David MacKenzie and Jim Meyering.