#===BEGIN Declare local script functions===
yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
-try() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
checkInt() {
# Desc: Checks if arg is integer
# Usage: checkInt arg
# Output: - return code 0 (if arg is integer)
# - return code 1 (if arg is not integer)
# Example: if ! checkInt $arg; then echo "not int"; fi;
- # Version: 0.0.1
+ # Version: 0.0.2
local returnState
#===Process Arg===
fi;
RETEST1='^[0-9]+$'; # Regular Expression to test
- if [[ ! $1 =~ $RETEST1 ]] ; then
+ if [[ ! "$1" =~ $RETEST1 ]] ; then
returnState="false";
else
returnState="true";
--- /dev/null
+#!/usr/bin/env bash
+# Desc: Insert string into provided string
+
+yell() { echo "$0: $*" >&2; }
+die() { yell "$*"; exit 111; }
+must() { "$@" || die "cannot $*"; }
+insertStr() {
+ # Desc: Inserts a string into another string at a specified position.
+ # Input: arg1: str str_rec String to receive insertion
+ # arg2: int pos Insertion position (0 = append to front)
+ # arg3: str str_ins String to be inserted
+ # Output: stdout: Combined string
+ # Version: 0.0.2
+ # Depends: BK-2020-03: yell(), die(), must()
+ # Ref/Attrib: * BK-2020-03: https://gitlab.com/baltakatei/baltakatei-exdev/
+ # * Cooper, Mendel. “Advanced Bash-Scripting Guide: Manipulating Strings”. tldp.org https://tldp.org/LDP/abs/html/string-manipulation.html
+
+ local str_rec pos str_ins re len_str_rec;
+ local pfx_pos_start pfx_len pfx;
+ local sfx_pos_start sfx_len sfx;
+
+ # Check args
+ if [[ $# -ne 3 ]]; then
+ yell "ERROR:Invalid argument count:$#";
+ return 1; fi;
+ re='^[0-9]+$';
+ if [[ ! "$2" =~ $re ]]; then
+ yell "ERROR:Not an int:$2";
+ return 1; fi;
+ str_rec="$1";
+ pos="$2";
+ str_ins="$3";
+
+ # Calculate string stats
+ len_str_rec="${#str_rec}";
+
+ # Form prefix
+ pfx_pos_start="0";
+ pfx_len="$pos";
+ pfx="${str_rec:$pfx_pos_start:$pfx_len}";
+
+ # Form suffix
+ sfx_pos_start="$(( pos ))";
+ sfx_len="$(( len_str_rec - pos ))";
+ sfx="${str_rec:$sfx_pos_start:$sfx_len}";
+
+ # Print output to stdout
+ printf "%s%s%s\n" "$pfx" "$str_ins" "$sfx";
+}; # Insert string provided at indicated position via stdout
+
+
+# Tests
+printf "\"%s\" should read \"foobarbaz\"\n" "$(insertStr "foobaz" 3 "bar";)";
+
+insertStr "foobaz" 0 "bar";
+insertStr "foobaz" 1 "bar";
+insertStr "foobaz" 2 "bar";
+insertStr "foobaz" 3 "bar";
+insertStr "foobaz" 4 "bar";
+insertStr "foobaz" 5 "bar";
+insertStr "foobaz" 6 "bar";
+insertStr "foobaz" 7 "bar";
+insertStr "foobaz" 999 "bar";
--- /dev/null
+#!/usr/bin/env python3
+# Desc: Humidity conversion functions from EVA-2020-02-2
+# Ref/Attrib: https://gitlab.com/baltakatei/ninfacyzga-01
+
+def rel_to_abs(T,P,RH):
+ """Returns absolute humidity given relative humidity.
+
+ Inputs:
+ --------
+ T : float
+ Absolute temperature in units Kelvin (K).
+ P : float
+ Total pressure in units Pascals (Pa).
+ RH : float
+ Relative humidity in units percent (%).
+
+ Output:
+ --------
+ absolute_humidity : float
+ Absolute humidity in units [kg water vapor / kg dry air].
+
+ References:
+ --------
+ 1. Sonntag, D. "Advancements in the field of hygrometry". 1994. https://doi.org/10.1127/metz/3/1994/51
+ 2. Green, D. "Perry's Chemical Engineers' Handbook" (8th Edition). Page "12-4". McGraw-Hill Professional Publishing. 2007.
+
+ Version: 0.0.1
+ Author: Steven Baltakatei Sandoval
+ License: GPLv3+
+ """
+
+ import math;
+
+ # Check input types
+ T = float(T);
+ P = float(P);
+ RH = float(RH);
+
+ #debug
+ # print('DEBUG:Input Temperature (K) :' + str(T));
+ # print('DEBUG:Input Pressure (Pa) :' + str(P));
+ # print('DEBUG:Input Rel. Humidity (%) :' + str(RH));
+
+ # Set constants and initial conversions
+ epsilon = 0.62198 # (molar mass of water vapor) / (molar mass of dry air)
+ t = T - 273.15; # Celsius from Kelvin
+ P_hpa = P / 100; # hectoPascals (hPa) from Pascals (Pa)
+
+ # Calculate e_w(T), saturation vapor pressure of water in a pure phase, in Pascals
+ ln_e_w = -6096*T**-1 + 21.2409642 - 2.711193*10**-2*T + 1.673952*10**-5*T**2 + 2.433502*math.log(T); # Sonntag-1994 eq 7; e_w in Pascals
+ e_w = math.exp(ln_e_w);
+ e_w_hpa = e_w / 100; # also save e_w in hectoPascals (hPa)
+ # print('DEBUG:ln_e_w:' + str(ln_e_w)); # debug
+ # print('DEBUG:e_w:' + str(e_w)); # debug
+
+ # Calculate f_w(P,T), enhancement factor for water
+ f_w = 1 + (10**-4*e_w_hpa)/(273 + t)*(((38 + 173*math.exp(-t/43))*(1 - (e_w_hpa / P_hpa))) + ((6.39 + 4.28*math.exp(-t / 107))*((P_hpa / e_w_hpa) - 1))); # Sonntag-1994 eq 22.
+ # print('DEBUG:f_w:' + str(f_w)); # debug
+
+ # Calculate e_prime_w(P,T), saturation vapor pressure of water in air-water mixture, in Pascals
+ e_prime_w = f_w * e_w; # Sonntag-1994 eq 18
+ # print('DEBUG:e_prime_w:' + str(e_prime_w)); # debug
+
+ # Calculate e_prime, vapor pressure of water in air, in Pascals
+ e_prime = (RH / 100) * e_prime_w;
+ # print('DEBUG:e_prime:' + str(e_prime)); # debug
+
+ # Calculate r, the absolute humidity, in [kg water vapor / kg dry air]
+ r = (epsilon * e_prime) / (P - e_prime);
+ # print('DEBUG:r:' + str(r)); # debug
+
+ return float(r);
+
+def rel_to_dpt(T,P,RH):
+ """Returns dew point temperature given relative humidity.
+
+ Inputs:
+ --------
+ T : float
+ Absolute temperature in units Kelvin (K).
+ P : float
+ Total pressure in units Pascals (Pa).
+ RH : float
+ Relative humidity in units percent (%).
+
+ Output:
+ --------
+ T_d : float
+ Dew point temperature in units Kelvin (K).
+
+ References:
+ --------
+ 1. Sonntag, D. "Advancements in the field of hygrometry". 1994. https://doi.org/10.1127/metz/3/1994/51
+ 2. Green, D. "Perry's Chemical Engineers' Handbook" (8th Edition). Page "12-4". McGraw-Hill Professional Publishing. 2007.
+
+ Version: 0.0.1
+ Author: Steven Baltakatei Sandoval
+ License: GPLv3+
+ """
+
+ import math;
+
+ # Check input types
+ T = float(T);
+ P = float(P);
+ RH = float(RH);
+
+ #debug
+ # print('DEBUG:Input Temperature (K) :' + str(T));
+ # print('DEBUG:Input Pressure (Pa) :' + str(P));
+ # print('DEBUG:Input Rel. Humidity (%) :' + str(RH));
+
+ # Set constants and initial conversions
+ epsilon = 0.62198 # (molar mass of water vapor) / (molar mass of dry air)
+ t = T - 273.15; # Celsius from Kelvin
+ P_hpa = P / 100; # hectoPascals (hPa) from Pascals (Pa)
+
+ # Calculate e_w(T), saturation vapor pressure of water in a pure phase, in Pascals
+ ln_e_w = -6096*T**-1 + 21.2409642 - 2.711193*10**-2*T + 1.673952*10**-5*T**2 + 2.433502*math.log(T); # Sonntag-1994 eq 7; e_w in Pascals
+ e_w = math.exp(ln_e_w);
+ e_w_hpa = e_w / 100; # also save e_w in hectoPascals (hPa)
+ # print('DEBUG:ln_e_w:' + str(ln_e_w)); # debug
+ # print('DEBUG:e_w:' + str(e_w)); # debug
+
+ # Calculate f_w(P,T), enhancement factor for water
+ f_w = 1 + (10**-4*e_w_hpa)/(273 + t)*(((38 + 173*math.exp(-t/43))*(1 - (e_w_hpa / P_hpa))) + ((6.39 + 4.28*math.exp(-t / 107))*((P_hpa / e_w_hpa) - 1))); # Sonntag-1994 eq 22.
+ # print('DEBUG:f_w:' + str(f_w)); # debug
+
+ # Calculate e_prime_w(P,T), saturation vapor pressure of water in air-water mixture, in Pascals
+ e_prime_w = f_w * e_w; # Sonntag-1994 eq 18
+ # print('DEBUG:e_prime_w:' + str(e_prime_w)); # debug
+
+ # Calculate e_prime, vapor pressure of water in air, in Pascals
+ e_prime = (RH / 100) * e_prime_w;
+ # print('DEBUG:e_prime:' + str(e_prime)); # debug
+
+ n = 0; repeat_flag = True;
+ while repeat_flag == True:
+ # print('DEBUG:n:' + str(n)); # debug
+
+ # Calculate f_w_td, the enhancement factor for water at dew point temperature.
+ if n == 0:
+ f = 1.0016 + 3.15*10**-6*P_hpa - (0.074 / P_hpa); # Sonntag-1994 eq 24
+ f_w_td = f; # initial approximation
+ elif n > 0:
+ t_d_prev = float(t_d); # save previous t_d value for later comparison
+ f_w_td = 1 + (10**-4*e_w_hpa)/(273 + t_d)*(((38 + 173*math.exp(-t_d/43))*(1 - (e_w_hpa / P_hpa))) + ((6.39 + 4.28*math.exp(-t_d / 107))*((P_hpa / e_w_hpa) - 1))); # Sonntag-1994 eq 22.
+ # print('DEBUG:f_w_td:' + str(f_w_td)); # debug
+
+ # Calculate e, the vapor pressure of water in the pure phase, in Pascals
+ e = (e_prime / f_w_td); # Sonntag-1994 eq 9 and 20
+ # print('DEBUG:e:' + str(e)); # debug
+
+ # Calculate y, an intermediate dew point calculation variable
+ y = math.log(e / 611.213);
+ # print('DEBUG:y:' + str(y)); # debug
+
+ # Calculate t_d, the dew point temperature in degrees Celsius
+ t_d = 13.715*y + 8.4262*10**-1*y**2 + 1.9048*10**-2*y**3 + 7.8158*10**-3*y**4;# Sonntag-1994 eq 10
+ # print('DEBUG:t_d:' + str(t_d)); # debug
+
+ if n == 0:
+ # First run
+ repeat_flag = True;
+ else:
+ # Test t_d accuracy
+ t_d_diff = math.fabs(t_d - t_d_prev);
+ # print('DEBUG:t_d :' + str(t_d)); # debug
+ # print('DEBUG:t_d_prev:' + str(t_d_prev)); # debug
+ # print('DEBUG:t_d_diff:' + str(t_d_diff)); # debug
+ if t_d_diff < 0.01:
+ repeat_flag = False;
+ else:
+ repeat_flag = True;
+
+ # Calculate T_d, the dew point temperature in Kelvin
+ T_d = 273.15 + t_d;
+ # print('DEBUG:T_d:' + str(T_d)); # debug
+
+ if n > 100:
+ return T_d; # good enough
+
+ # update loop counter
+ n += 1;
+ return T_d;
#!/usr/bin/env bash
# Desc: Copies random audio files
# Usage: bk-copy-rand-music [dir SOURCE] [dir DEST] [int DURATION] ([int BYTES])
-# Version: 0.1.1
+# Version: 0.2.0
# Depends: BK-2020-03: bkshuf v0.1.0
declare -Ag appRollCall # Associative array for storing app status
fpath="$(printf "%s" "$line" | cut -d',' -f3-)";
## Get basename of path
file_basename="$(basename "$fpath")";
+ ### Get basename without unprintable non-ASCII characters
+ file_basename_compat="$(printf "%s" "$file_basename" | tr -dc '[:graph:][:space:]' )";
## Get 16-character b2sum fingerprint (for different files that share basename)
fingerprint="$(b2sum -l32 "$fpath" | awk '{print $1}' )";
## Form output filename
num="$(printf "$num_fmt" "$n")";
- file_name="$num"_"$fingerprint".."$file_basename";
+ file_name="$num"_"$fingerprint".."$file_basename_compat";
file_name="${file_name:0:$max_filename_length}"; # Limit filename length (e.g. Windows has max of 255 characters)
## Form output path
printf "$log_fmt" "$num" "$fingerprint" "$fdur" "$fsize" "$fpath_can" >> "$path_log_output";
((n++));
- unset file_basename path_output
+ unset file_basename file_basename_compat path_output;
done < <(printf "%s\n" "${list_copy[@]}");
# Report total duration and size
# Finds and verifies signatures of git repositories in specified dir
# Usage: bk-find-git-verify [DIR]
# Depends: GNU parallel 20210822, sort (GNU coreutils) 8.32
-# Version: 0.0.1
+# Version: 0.0.2
+
+# global vars
+findMaxDepth=8;
# Define functions
yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
if [[ ! -d "$1" ]]; then die "FATAL:Not a dir:$1"; fi;
# Get list of dirs containing '.git' directory
- dir_list="$(find "$1" -type d -name ".git" 2>/dev/random | parallel dirname '{}' | sort -u)";
+ dir_list="$(find -L "$1" -maxdepth "$findMaxDepth" -type d -name ".git" 2>/dev/random | parallel readlink -f '{}' | sort -u | parallel dirname '{}' | sort -u)";
# Perform git verify operations on each directory
printf "%s" "$dir_list" | parallel git_verify_ops '{}';
# Desc: Baltakatei's verbose date command
# Usage: bkdatev [args]
# Example: bkdatev --date="2001-09-11T09:02:59-04"
-# Version: 0.2.2
+# Version: 0.3.4
+# Depends: GNU Coreutils 8.32, Bash 3.2.57
# Ref/Attrib: [1] "ISO 8601". Wikipedia. https://en.wikipedia.org/wiki/ISO_8601
# [2] "Changing the Locale in Wine" https://stackoverflow.com/a/16428951
# [3] "Shanghai vs Beijing" https://bugs.launchpad.net/ubuntu/+source/libgweather/+bug/228554
# * For list of valid locales, see: https://manpages.ubuntu.com/manpages/bionic/man3/DateTime::Locale::Catalog.3pm.html
# * Locations chosen for population, personal signifiance, and spatial coverage.
# * For International Atomic Time (TAI), use offsets from UTC provided in `/usr/share/zoneinfo/leap-seconds.list`.
-
+# * Compatibility with macOS may be limited if any arguments
+# are provided when running `bkdatev`; e.g. passing a
+# `--date` option to `bkdatev` will fail.
+
+yell() { echo "$0: $*" >&2; }
+die() { yell "$*"; exit 111; }
+must() { "$@" || die "cannot $*"; }
+insertStr() {
+ # Desc: Inserts a string into another string at a specified position.
+ # Input: arg1: str str_rec String to receive insertion
+ # arg2: int pos Insertion position (0 = append to front)
+ # arg3: str str_ins String to be inserted
+ # Output: stdout: Combined string
+ # Version: 0.0.2
+ # Depends: BK-2020-03: yell(), die(), must()
+ # Ref/Attrib: * BK-2020-03: https://gitlab.com/baltakatei/baltakatei-exdev/
+ # * Cooper, Mendel. “Advanced Bash-Scripting Guide: Manipulating Strings”. tldp.org https://tldp.org/LDP/abs/html/string-manipulation.html
+
+ local str_rec pos str_ins re len_str_rec;
+ local pfx_pos_start pfx_len pfx;
+ local sfx_pos_start sfx_len sfx;
+
+ # Check args
+ if [[ $# -ne 3 ]]; then
+ yell "ERROR:Invalid argument count:$#";
+ return 1; fi;
+ re='^[0-9]+$';
+ if [[ ! "$2" =~ $re ]]; then
+ yell "ERROR:Not an int:$2";
+ return 1; fi;
+ str_rec="$1";
+ pos="$2";
+ str_ins="$3";
+
+ # Calculate string stats
+ len_str_rec="${#str_rec}";
+
+ # Form prefix
+ pfx_pos_start="0";
+ pfx_len="$pos";
+ pfx="${str_rec:$pfx_pos_start:$pfx_len}";
+
+ # Form suffix
+ sfx_pos_start="$(( pos ))";
+ sfx_len="$(( len_str_rec - pos ))";
+ sfx="${str_rec:$sfx_pos_start:$sfx_len}";
+
+ # Print output to stdout
+ printf "%s%s%s\n" "$pfx" "$str_ins" "$sfx";
+}; # Insert string provided at indicated position via stdout
line_sep() {
# Input: var: n_ln
local skip_every=4;
fi;
return 0;
}; # periodically print separating blank line
+get_tz_offset() {
+ # Desc: Get from 'date' the timezone UTC offset in a way
+ # compatible with both GNU Coreutils and BSD versions.
+ # Input: env var: TZ (time zone for date; e.g. 'America/Denver')
+ # args: $@ # passed onto `date`
+ # Depends: date (GNU Coreutils 8.32 or BSD), rev
+ local ntz ntz ntz_out;
+ local last2;
+
+ # Get numeric time zone string in way compatible with GNU Coreutils and BSD
+ ntz="$(date "+%z" "$@")"; # e.g. "+0530"
+
+ # Check if last two characters are trailing zeros that can be removed.
+ last2="${ntz:3:2}"; # assumes $ntz is 5 characters (i.e. "±HHMM")
+ #last2="$(rev <<< $ntz)" && last2="${last2:0:2}" && last2="$(rev <<< "$last2")";
+ if [[ "$last2" == "00" ]]; then
+ ## ntz_out is truncated by 2 characters
+ len_ntz="${#ntz}";
+ len_ntz_out="$(( len_ntz - 2 ))";
+ ntz_out=""${ntz:0:$len_ntz_out};
+ else
+ ## ntz_out is ntz with semicolon inserted after HH
+ ntz_out="$(insertStr "$ntz" 3 ":" )";
+ fi;
+
+ # Output via stdout
+ printf "%s" "$ntz_out";
+}; # Format numeric time zone (for BSD date compatibility)
print_dateline() {
# Input: var: $id
# var: $fs_1
# var: $fs_2
# var: $fs_3
- # args: $@
+ # args: $@ # passed on to `date`
+ # env var: TZ (time zone for date; e.g. 'America/Denver')
# Output: stdout
# Depends: printf, date
+ # get_tz_offset()
# Ref/Attrib: * Truncate string in printf https://stackoverflow.com/a/46812677
- local s_1 s_2 s_3 s_4;
+ local s_1 s_2 s_2_tz s_3 s_4;
s_1="$id";
- s_2="$(date "$@" "$fs_1")";
- s_3="$(date "$@" "$fs_2")";
- s_4="$(date "$@" "$fs_3")";
+ s_2="$(date "$@" "$fs_1")"; # ISO-8601 without numeric timezone
+ s_3="$(date "$@" "$fs_2")"; # Alternate ISO-8601 expressions
+ s_4="$(date "$@" "$fs_3")"; # locale-specific date strings
+
+ # Append numeric timezone to $s_2 with appropriate format
+ # (e.g. '-07' for 'Arizona', '+05:45' for 'Asia/Kathmandu')
+ s_2_tz="$(get_tz_offset "$@")";
+ s_2="$( printf "%s%s" "$s_2" "$s_2_tz" )";
printf "%-10.10s %-25.25s (%-20.20s) (%s)" "$s_1" "$s_2" "$s_3" "$s_4";
printf "\n";
unset LC_TIME; # Fall back to time zone-specific locale settings.
# format strings
- fs_iso8601="+%Y-%m-%dT%H:%M:%S%:::z"; # typical ISO-8601
+ fs_iso8601="+%Y-%m-%dT%H:%M:%S"; # typical ISO-8601 without timezone
fs_iso8601_etc="+%G-W%V-%u, %Y-%j"; # alternate ISO-8601 dates
fs_locale="+%Z; %A; %c"; # locale-specific date strings
print_dateline "$@";
); line_sep;
+ # Helsinki, Finland
+ (
+ export TZ=Europe/Helsinki;
+ export LANG="fi_FI.UTF8";
+ id="HELSINKI";
+ print_dateline "$@";
+ ); line_sep;
+
# Cairo, Egypt
(
export TZ=Africa/Cairo;
}; # main program
main "$@";
+
+# Author: Steven Baltakatei Sandoval
+# License: GPLv3+
--- /dev/null
+#!/usr/bin/env bash
+# Desc: Combines PDF files into a single PDF file
+# Depends: pdftk 2.02-5 ( https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/ )
+# Version 0.0.1
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+showUsage() {
+ # Desc: Display script usage information
+ # Usage: showUsage
+ # Version 0.0.2
+ # Input: none
+ # Output: stdout
+ # Depends: GNU-coreutils 8.30 (cat)
+ cat <<'EOF'
+ USAGE:
+ combine_pdfs.sh [FILE output] [FILES input...]
+
+ EXAMPLE:
+ combine_pdfs.sh output.pdf input1.pdf input2.pdf
+ combine_pdfs.sh output.pdf input*.pdf
+EOF
+} # Display information on how to use this script.
+checkDepends() {
+ if ! command -v pdftk 1>/dev/random 2>&1; then
+ showUsage;
+ die "FATAL:Missing pdftk."; fi;
+}; # Check dependencies
+checkArgs() {
+ local narg;
+
+ # Check arguments
+ narg=0;
+ for arg in "$@"; do
+ #yell "DEBUG:Checking argument:$arg";
+ if [[ $narg -le 0 ]]; then
+ if [[ ! -f "$arg" ]]; then
+ ((narg++)); continue;
+ else
+ ## Get permission to overwrite existing output file
+ yell "WARNING:Overwrite output file \"$arg\"? (y/n)";
+ read -r prompt;
+ case "$prompt" in
+ "y" | "yes" | "Yes" | "YES" )
+ ;;
+ "n" | "no" | "No" | "NO" )
+ showUsage; die "FATAL:Aborted.";
+ ;;
+ *)
+ showUsage; die "FATAL:Invalid response.";
+ ;;
+ esac;
+ fi;
+ fi; # handle first arg
+
+ if [[ ! -f "$arg" ]]; then
+ showUsage; die "FATAL:File does not exist:$arg"; fi;
+ ((narg++));
+ done;
+
+ return 0;
+};
+combinePDFs() {
+ # Desc: Combines PDFs and writes to output PDF file
+ # Depends: pdftk 2.02-5
+ local cmd path_fo;
+ local -a paths_fi;
+
+ # Save output file path
+ path_fo="$1";
+
+ # Form array of input file paths
+ paths_fi=("$@");
+ unset 'paths_fi[0]'; # discard first path which is output file
+
+ # Form command array
+ cmd+=("pdftk"); # executable
+ cmd+=("${paths_fi[@]}"); # input file paths
+ cmd+=("cat" "output");
+ cmd+=("$path_fo");
+ #yell "DEBUG:cmd:$(declare -p cmd)";
+ #yell "DEBUG:cmd:${cmd[*]}";
+
+ # Execute command array
+ "${cmd[@]}";
+}; # Combines PDFs and writes to output PDF file
+main() {
+ #yell "DEBUG:$(args=("$@"); declare -p args)";
+ checkDepends;
+ checkArgs "$@";
+ combinePDFs "$@";
+ exit 0;
+}; # main program
+
+main "$@";
+
+# Author: Steven Baltakatei Sandoval
+# License: GPLv3+
+
+# # Copyright notices
+
+# pdftk port to java 3.2.2 a Handy Tool for Manipulating PDF Documents
+# Copyright (c) 2017-2018 Marc Vinyals - https://gitlab.com/pdftk-java/pdftk
+# Copyright (c) 2003-2013 Steward and Lee, LLC.
+# pdftk includes a modified version of the iText library.
+# Copyright (c) 1999-2009 Bruno Lowagie, Paulo Soares, et al.
+# This is free software; see the source code for copying conditions. There is
+# NO warranty, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
--- /dev/null
+#!/bin/bash
+# Desc: Converts a directory of mp3s into a single mkv file
+# Usage: mp3s_to_mkv.sh [DIR in] [DIR out] [BITRATE]
+# Example: mp3s_to_mkv.sh ./dir_source ./dir_output 48k
+# Depends: GNU Coretils 8.32 (date)
+# Version: 0.0.1
+
+# plumbing
+opus_bitrate="$3"; # e.g. "48k"
+script_rundate="$(date +%s)";
+dir_tmp="/dev/shm";
+dir_in="$(readlink -f "$1")";
+dir_out="$(readlink -f "$2")";
+file_flist="$dir_tmp"/"$script_rundate"..flist.txt;
+file_out_mkv="$dir_out"/output.mkv;
+file_albumart="$dir_out"/albumart.png;
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+show_usage() {
+ # Desc: Display script usage information
+ # Usage: showUsage
+ # Version 0.0.2
+ # Input: none
+ # Output: stdout
+ # Depends: GNU-coreutils 8.30 (cat)
+ cat <<'EOF'
+ USAGE:
+ mp3s_to_mkv.sh [DIR in] [DIR out] [BITRATE]
+
+ EXAMPLE:
+ mp3s_to_mkv.sh ./source_dir ./out_dir 48k
+EOF
+} # Display information on how to use this script.
+check_depends() {
+ if ! command -v ffmpeg; then show_usage; die "FATAL:Missing ffmpeg."; fi;
+}; # check dependencies
+check_plumbing() {
+ if [[ $# -ne 3 ]]; then show_usage; die "FATAL:Invalid arg count:$#"; fi;
+ if [[ ! -d "$dir_in" ]]; then show_usage; die "FATAL:Not a dir:$dir_in"; fi;
+ if [[ ! -d "$dir_out" ]]; then mkdir -p "$2"; fi;
+}; # check arguments
+build_filelist() {
+ # Depends: var dir_tmp temporary directory
+ # var dir_in input dir
+ # var file_flist path file list
+ # Output: file: $file_flist list of mp3 files for ffmpeg
+
+ # Change directory to input dir
+ pushd "$dir_in" || die "FATAL:Directory error:$(pwd)";
+
+ while read -r line; do
+ yell "$(printf "file '%s'\n" "${line#./}")";
+ printf "file '%s'\n" "${line#./}" >> "$file_flist";
+ done < <(find "$dir_in" -type f -iname "*.mp3" | sort);
+
+ # Return to original dir
+ popd || die "FATAL:Directory error:$(pwd)";
+
+}; # build file list for ffmpeg
+ffmpeg_convert() {
+ # Depends: var dir_tmp
+ # dir_in
+ # dir_out
+ # Input: file $file_flist list of mp3 files for ffmpeg
+
+ # Change directory to input dir
+ pushd "$dir_in" || die "FATAL:Directory error:$(pwd)";
+
+ # Concatenate mp3 files into a single WAV file
+ # # Convert WAV to 48 kbps opus mkv file
+ ffmpeg -f concat -safe 0 -i "$file_flist" -c:a pcm_s24le -rf64 auto -f wav - | \
+ ffmpeg -i - -c:a libopus -b:a "$opus_bitrate" "$file_out_mkv";
+
+ # Return to original dir
+ popd || die "FATAL:Directory error:$(pwd)";
+}; # convert mp3s to opus mkv via ffmpeg
+save_albumart() {
+ local file
+ file="$(find "$dir_in" -type f -iname "*.mp3" | sort | head -n1)";
+ file="$(readlink -f "$file")";
+ ffmpeg -i "$file" -an -vcodec copy "$file_albumart";
+}; # save album art from an mp3 to output dir
+main() {
+ check_depends && yell "DEBUG:check_depends OK";
+ check_plumbing "$@" && yell "DEBUG:check_plumbing OK";
+ build_filelist "$@" && yell "DEBUG:build_filelist OK";
+ ffmpeg_convert "$@" && yell "DEBUG:ffmpeg_convert OK";
+ save_albumart "$@" && yell "DEBUG:save_albumart OK";
+}; # main program
+
+main "$@";
+
--- /dev/null
+#!/bin/bash
+# Desc: Converts a directory of mp3s into a single opus file
+# Usage: mp3s_to_opus.sh [DIR in] [DIR out] [BITRATE]
+# Example: mp3s_to_opus.sh ./dir_source ./dir_output 48k
+# Depends: GNU Coretils 8.32 (date)
+# Version: 0.0.3
+
+# plumbing
+opus_bitrate="$3"; # e.g. "48k"
+script_rundate="$(date +%s)";
+dir_tmp="/dev/shm";
+dir_in="$(readlink -f "$1")";
+dir_out="$(readlink -f "$2")";
+file_flist="$dir_tmp"/"$script_rundate"..flist.txt;
+file_out_opus="$dir_out"/output.opus;
+file_albumart="$dir_out"/albumart.png;
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+show_usage() {
+ # Desc: Display script usage information
+ # Usage: showUsage
+ # Version 0.0.2
+ # Input: none
+ # Output: stdout
+ # Depends: GNU-coreutils 8.30 (cat)
+ cat <<'EOF'
+ USAGE:
+ mp3s_to_opus.sh [DIR in] [DIR out] [BITRATE]
+
+ EXAMPLE:
+ mp3s_to_opus.sh ./source_dir ./out_dir 48k
+EOF
+} # Display information on how to use this script.
+check_depends() {
+ if ! command -v ffmpeg; then show_usage; die "FATAL:Missing ffmpeg."; fi;
+}; # check dependencies
+check_plumbing() {
+ if [[ $# -ne 3 ]]; then show_usage; die "FATAL:Invalid arg count:$#"; fi;
+ if [[ ! -d "$dir_in" ]]; then show_usage; die "FATAL:Not a dir:$dir_in"; fi;
+ if [[ ! -d "$dir_out" ]]; then mkdir -p "$2"; fi;
+}; # check arguments
+build_filelist() {
+ # Depends: var dir_tmp temporary directory
+ # var dir_in input dir
+ # var file_flist path file list
+ # Output: file: $file_flist list of mp3 files for ffmpeg
+
+ # Change directory to input dir
+ pushd "$dir_in" || die "FATAL:Directory error:$(pwd)";
+
+ while read -r line; do
+ yell "$(printf "file '%s'\n" "${line#./}")";
+ printf "file '%s'\n" "${line#./}" >> "$file_flist";
+ done < <(find "$dir_in" -type f -iname "*.mp3" | sort);
+
+ # Return to original dir
+ popd || die "FATAL:Directory error:$(pwd)";
+
+}; # build file list for ffmpeg
+ffmpeg_convert() {
+ # Depends: var dir_tmp
+ # dir_in
+ # dir_out
+ # Input: file $file_flist list of mp3 files for ffmpeg
+
+ # Change directory to input dir
+ pushd "$dir_in" || die "FATAL:Directory error:$(pwd)";
+
+ # Concatenate mp3 files into a single WAV file
+ # # Convert WAV to 48 kbps opus file
+ ffmpeg -f concat -safe 0 -i "$file_flist" -c:a pcm_s24le -rf64 auto -f wav - | \
+ ffmpeg -i - -c:a libopus -b:a "$opus_bitrate" "$file_out_opus";
+
+ # Return to original dir
+ popd || die "FATAL:Directory error:$(pwd)";
+}; # convert mp3s to opus via ffmpeg
+save_albumart() {
+ local file
+ file="$(find "$dir_in" -type f -iname "*.mp3" | sort | head -n1)";
+ file="$(readlink -f "$file")";
+ ffmpeg -i "$file" -an -vcodec copy "$file_albumart";
+}; # save album art from an mp3 to output dir
+main() {
+ check_depends && yell "DEBUG:check_depends OK";
+ check_plumbing "$@" && yell "DEBUG:check_plumbing OK";
+ build_filelist "$@" && yell "DEBUG:build_filelist OK";
+ ffmpeg_convert "$@" && yell "DEBUG:ffmpeg_convert OK";
+ save_albumart "$@" && yell "DEBUG:save_albumart OK";
+}; # main program
+
+main "$@";
+
# Input: arg1: year
# arg2: month
# Example: /bin/bash mw_create_month_journal.sh 2023 03 > output.txt
-# Version: 0.0.1
+# Version: 0.1.0
yyyy="$1";
mm="$2";
fi;
# Form and print header
+printf "{{bk journal header month}}\n";
printf "Journal for [[%d]]-%02d. " "$yyyy" "$mm";
printf "Preceded by [[%d-%02d]]. " "$yyyy_prev" "$mm_prev";
printf "Followed by [[%d-%02d]]. " "$yyyy_next" "$mm_next";
printf "==Ext. Links==\n";
printf "\n";
+printf "{{bk journal footer month}}\n";
+
s1="[[Category:Journals by month]]";
printf "%s\n" "$s1";
printf "\n";
--- /dev/null
+#!/usr/bin/env bash
+# Desc: Wrapper for creating monthly journal page generation script
+# Input: arg1: path mw_create_month_journal.sh
+# arg2: year start
+# arg3: year end
+# Example: mw_create_month_journal_range.sh ./mw_create_month_journal.sh 2030 2040
+# Depends: BK-2020-03: mw_create_month_journal.sh
+# Version: 1.0.0
+
+# plumbing
+path_cmj="$1"; # eg "$HOME/scripts/mw_create_month_journal.sh"
+year_start="$2"; # eg 2030
+year_end="$3"; # eg 2040
+dir_out="./wikicode/";
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+check_depends() {
+ # check location of `mw_create_month_journal.sh`
+ if [[ ! -f "$path_cmj" ]]; then die "FATAL:Not found:$path_cmj"; fi;
+};
+check_plumbing() {
+ if [[ ! -d "$dir_out" ]]; then
+ yell "STATUS:Creating missing dir_out:$dir_out";
+ mkdir -p "$dir_out"; fi;
+};
+main() {
+ check_depends;
+ check_plumbing;
+
+ while read -r year; do
+ while read -r month; do
+ monthp="$(printf "%2d" "$month")";
+ # Define and execute create monthly journal command
+ cmj_args+=("$year");
+ cmj_args+=("$month");
+
+ # Define output file path
+ path_out="$dir_out"/"$(printf "%d-%02d" "$year" "$month")";
+
+ # Execute
+ "$path_cmj" "${cmj_args[@]}" > "$path_out";
+
+ unset cmj_args;
+ done < <(seq 1 12); # each month
+ done < <(seq "$year_start" "$year_end"); # each year
+};
+
+main "$@";
+
+# Author: Steven Baltakatei Sandoval
+# License: GPlv3+
--- /dev/null
+#!/usr/bin/env bash
+# Desc: Prints mediawiki code for a journal year in the bk4 wiki.
+# Usage: /bin/bash mw_create_year_journal.sh [int year]
+# Input: arg1: year
+# Example: /bin/bash mw_create_year_journal.sh 2023 > output.txt
+# Version: 0.0.1
+
+yyyy="$1";
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+
+# Check input
+## Check arg count
+if [[ $# -ne 1 ]]; then die "FATAL:Invalid arg count:$#"; fi;
+## Strip leading zeros
+yyyy="$((10#${yyyy}))";
+
+## Check year
+re="[0-9]+";
+if [[ ! $yyyy =~ $re ]]; then die "FATAL:Invalid year string:$yyyy"; fi;
+if [[ ! $yyyy -ge 1582 ]]; then die "FATAL:Invalid year string:$yyyy"; fi;
+
+# Calc working vars
+## Years
+yyyy_prev="$((yyyy - 1))";
+yyyy_next="$((yyyy + 1))";
+
+# Form and print header
+printf "{{bk journal header year}}\n";
+printf "[[Journal]] for the year [[%d]]. " "$yyyy";
+printf "Preceded by [[%d]]. " "$yyyy_prev";
+printf "Followed by [[%d]]. " "$yyyy_next";
+printf "\n\n";
+
+printf "==Events==\n";
+printf "\n";
+
+# Form and print body
+for (( mm=1; mm <= 12; mm++ )); do
+ printf "==[[%4d-%02d]]==\n" "$yyyy" "$mm";
+done;
+printf "\n";
+
+# Form and print footer
+s1="==References==";
+s2="<references>";
+s3="</references>";
+printf "%s\n%s\n%s\n" "$s1" "$s2" "$s3";
+printf "\n";
+
+printf "==See Also==\n";
+printf "\n";
+
+printf "==Ext. Links==\n";
+printf "\n";
+
+printf "{{bk journal footer year}}\n";
+
+s1="[[Category:Journals by year]]";
+printf "%s\n" "$s1";
+printf "\n";
--- /dev/null
+#!/usr/bin/env bash
+# Desc: Wrapper for creating yearly journal page generation script
+# Input: arg1: path mw_create_year_journal.sh
+# arg2: year start
+# arg3: year end
+# Example: mw_create_year_journal_range.sh ./mw_create_year_journal.sh 2030 2040
+# Depends: BK-2020-03: mw_create_year_journal.sh
+# Version: 0.0.1
+
+# plumbing
+path_cyj="$1"; # eg "$HOME/scripts/mw_create_year_journal.sh"
+year_start="$2"; # eg 2030
+year_end="$3"; # eg 2040
+dir_out="./wikicode/";
+
+yell() { echo "$0: $*" >&2; } # print script path and all args to stderr
+die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status
+must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails
+check_depends() {
+ # check location of `mw_create_year_journal.sh`
+ if [[ ! -f "$path_cyj" ]]; then die "FATAL:Not found:$path_cyj"; fi;
+};
+check_plumbing() {
+ if [[ ! -d "$dir_out" ]]; then
+ yell "STATUS:Creating missing dir_out:$dir_out";
+ mkdir -p "$dir_out"; fi;
+};
+main() {
+ check_depends;
+ check_plumbing;
+
+ while read -r year; do
+ cyj_args+=("$year");
+
+ # Define output file path
+ path_out="$dir_out"/"$(printf "%d" "$year")";
+
+ # Execute
+ "$path_cyj" "${cyj_args[@]}" > "$path_out";
+
+ unset cyj_args;
+ done < <(seq "$year_start" "$year_end"); # each year
+};
+
+main "$@";
+
+# Author: Steven Baltakatei Sandoval
+# License: GPlv3+