feat(user/bkots):Execute via array elements, not string concat
[BK-2020-03.git] / user / bkots
index d37b9c22cf41a6bca959e0db49a05e031584fbe6..cca1fd803ec7a11bc0169351e951be0caaa02b2c 100755 (executable)
@@ -10,13 +10,13 @@ calendars+=("https://finney.calendar.eternitywall.com");
 calendars+=("https://btc.calendar.catallaxy.com");
 calendars+=("https://alice.btc.calendar.opentimestamps.org");
 calendars+=("https://bob.btc.calendar.opentimestamps.org");
-declare -a commands # array for storing assembled commands
 age_threshold="60"; # min age to add file; seconds;
+max_job_count="2"; # default max job count
 
 # Declare 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
 checkapp() {
     # Desc: If arg is a command, save result in assoc array 'appRollCall'
     # Usage: checkapp arg1 arg2 arg3 ...
@@ -200,7 +200,7 @@ showVersion() {
     vbm "DEBUG:showVersion function called."
 
     cat <<'EOF'
-bkots 1.0.0
+bkots 2.0.0
 Copyright (C) 2022 Steven Baltakatei Sandoval
 License GPLv3: GNU GPL version 3
 This is free software; you are free to change and redistribute it.
@@ -239,6 +239,8 @@ showUsage() {
         --include-dotfiles
                 Include files and directories starting with '.' (not
                 included by default).
+        -j, --jobs
+                Specify simultaneous job count (default: 2)
         -r, --recursive
                 Consider files in dirs recursively.
         --version
@@ -268,6 +270,32 @@ showUsage() {
       bkots foo.txt bar.pdf /home/username/Pictures/
 EOF
 } # Display information on how to use this script.
+count_jobs() {
+    # Desc: Count and return total number of jobs
+    # Usage: count_jobs
+    # Input: None.
+    # Output: stdout   integer number of jobs
+    # Depends: Bash 5.1.16
+    # Example: while [[$(count_jobs) -gt 0]]; do echo "Working..."; sleep 1; done;
+    # Version: 0.0.1
+
+    local job_count;
+    job_count="$(jobs -r | wc -l | tr -d ' ' )";
+    #yell "DEBUG:job_count:$job_count";
+    if [[ -z $job_count ]]; then job_count="0"; fi;
+    echo "$job_count";
+}; # Return number of background jobs
+wait_for_jobslot() {
+    # Desc: Does not return until count_jobs() falls below $max_job_count
+    # Input: var max_job_count
+    # Output: return code 0
+    # Depends: count_jobs(), yell();
+    while [[ $(count_jobs) -ge $max_job_count ]]; do
+        printf "\r%d:Working..." "$SECONDS";
+        sleep 1;
+    done;
+    return 0;
+};
 processArgs() {
     # Desc: Processes arguments provided to script.
     # Usage: processArgs "$@"
@@ -302,6 +330,15 @@ processArgs() {
             --include-dotfiles) # Include dotfiles
                 option_include_dotfiles="true";
                 vbm "DEBUG:Option enabled:include dotfiles";;
+            -j | --jobs) # Specify max_job_count
+                if [[ -n "$2" ]] && [[ "$2" =~ ^[0-9]+$ ]]; then
+                    max_job_count="$2";
+                    vbm "STATUS:Max job count set to:$max_job_count";
+                    shift;
+                else
+                    showUsage;
+                    die "FATAL:Invalid job count:$2";
+                fi;;
             -r | --recursive) # Specify recursive option
                 option_recursive="true";
                 vbm "DEBUG:option enabled:include files in dirs recursively";;
@@ -336,7 +373,7 @@ get_parent_dirnames() {
     # Input: arg1  input  path
     # Output: stdout   newline-delimited list of parent dirs
     # Version: 0.0.1
-    # Depends: yell(), die(), try()
+    # Depends: yell(), die(), must()
     local path
 
     # Check input
@@ -354,14 +391,6 @@ get_parent_dirnames() {
         echo "$name_base";
     done;    
 }; # Output parent dirnames to stdout
-cmdwrap() {
-    # print command to stderr
-    echo "$@" 1>&2;
-
-    # execute command
-    "$@";
-}; # print and execute string together
-export -f cmdwrap; # export cmdwrap for use in other functions
 main() {
     # Desc: Creates `.ots` file:
     #     - for each file specified in arrayPosArgs array
@@ -640,7 +669,7 @@ main() {
     fi;
     
     # Act on files
-    ## Assemble upgrade file commands
+    ## Assemble and execute upgrade file commands
     for item in "${files_to_upgrade_pruned[@]}"; do
         path_prf="$(cut -d $'\n' -f1 < <(echo "$item"))";
         path_prf_sesc="${path_prf//\"/\\\"}"; # escape path double quotes. See [4].
@@ -653,11 +682,19 @@ main() {
             ### Try upgrade with known calendars in random order
             while read -r url; do
                 vbm "DEBUG:Upgrading with calendar:url:$url";
-                if [[ "$opVerbose" = "true" ]]; then
-                    commands+=("cmdwrap ots -v -l $url --no-default-whitelist upgrade \"$path_prf_sesc\"") && break;
-                else
-                    commands+=("cmdwrap ots -l $url --no-default-whitelist upgrade \"$path_prf_sesc\"") && break;
-                fi;
+
+                #### assemble command
+                local -a cmd_temp;
+                cmd_temp+=("ots");
+                if [[ "$opVerbose" = "true" ]]; then cmd_temp+=("-v"); fi;
+                cmd_temp+=("-l" "$url" "--no-default-whitelist");
+                cmd_temp+=("upgrade" "$path_prf_sesc");
+                if [[ "$opVerbose" = "true" ]]; then declare -p cmd_temp; fi;
+
+                #### execute command
+                wait_for_jobslot && must "${cmd_temp[@]}" &
+                unset cmd_temp;
+                break;
                 #ots -l "$url" --no-default-whitelist upgrade "$path_prf" && break;
             done < <(printf "%s\n" "${calendars[@]}" | shuf);
         else
@@ -665,7 +702,7 @@ main() {
         fi;
     done;
 
-    ## Assemble verify file commands
+    ## Assemble and execute verify file commands
     for item in "${files_to_verify_pruned[@]}"; do
         path_src="$(cut -d $'\n' -f1 < <(echo "$item"))";
         path_prf="$(cut -d $'\n' -f2 < <(echo "$item"))";
@@ -681,11 +718,19 @@ main() {
             ### Try verify with known calendars in random order
             while read -r url; do
                 vbm "DEBUG:Verifying with calendar:url:$url";
-                if [[ "$opVerbose" = "true" ]]; then
-                    commands+=("cmdwrap ots -v -l $url --no-default-whitelist verify -f \"$path_src_sesc\" \"$path_prf_sesc\"") && break;
-                else
-                    commands+=("cmdwrap ots -l $url --no-default-whitelist verify -f \"$path_src_sesc\" \"$path_prf_sesc\"") && break;
-                fi;
+
+                #### assemble command
+                local -a cmd_temp;
+                cmd_temp+=("ots");
+                if [[ "$opVerbose" = "true" ]]; then cmd_temp+=("-v"); fi;
+                cmd_temp+=("-l" "$url" "--no-default-whitelist");
+                cmd_temp+=("verify" "-f" "$path_src_sesc" "$path_prf_sesc");
+                if [[ "$opVerbose" = "true" ]]; then declare -p cmd_temp; fi;
+
+                #### execute command
+                wait_for_jobslot && must "${cmd_temp[@]}" &
+                unset cmd_temp;
+                break;
                 #ots -l "$url" --no-default-whitelist verify -f "$path_src" "$path_prf" && break;
             done < <(printf "%s\n" "${calendars[@]}" | shuf);
         else
@@ -693,7 +738,7 @@ main() {
         fi;
     done;
     
-    ## Assemble stamp file commands
+    ## Assemble and execute stamp file commands
     for item in "${files_to_stamp_pruned[@]}"; do
         path_src="$(cut -d $'\n' -f1 < <(echo "$item"))";
         path_src_sesc="${path_src//\"/\\\"}"; # escape path double quotes. See [4].
@@ -703,22 +748,24 @@ main() {
         fi;
         vbm "DEBUG:Attempting to stamp source file:path_src:$path_src";
         if [[ ! $option_dry_run == "true" ]]; then
-            if [[ "$opVerbose" = "true" ]]; then
-                commands+=("cmdwrap ots -v stamp \"$path_src_sesc\"");
-            else
-                commands+=("cmdwrap ots stamp \"$path_src_sesc\"");
-            fi;
+
+            #### assemble command
+            local -a cmd_temp;
+            cmd_temp+=("ots");
+            if [[ "$opVerbose" = "true" ]]; then cmd_temp+=("-v"); fi;
+            cmd_temp+=("stamp" "$path_src_sesc");
+            if [[ "$opVerbose" = "true" ]]; then declare -p cmd_temp; fi;
+
+            #### execute command
+            wait_for_jobslot && must "${cmd_temp[@]}" &
+            unset cmd_temp;
             #ots stamp "$path_src";
         else
             yell "DEBUG:DRY RUN:Not running:\"ots stamp $path_src\"";
         fi;
     done;
-
-    ## Run commands
-    yell "DEBUG:commands:$(printf "%s\n" "${commands[@]}")";
-    printf "%s\n" "${commands[@]}" | parallel --group;
-
 }; # main program
 
 # Run program
 main "$@";
+exit 0;