feat(unitproc/palign.sh):Add script to align path lists
authorSteven Baltakatei Sandoval <baltakatei@gmail.com>
Wed, 7 Aug 2024 09:45:31 +0000 (09:45 +0000)
committerSteven Baltakatei Sandoval <baltakatei@gmail.com>
Wed, 7 Aug 2024 09:45:31 +0000 (09:45 +0000)
unitproc/palign.sh [new file with mode: 0755]

diff --git a/unitproc/palign.sh b/unitproc/palign.sh
new file mode 100755 (executable)
index 0000000..99bad70
--- /dev/null
@@ -0,0 +1,129 @@
+#!/bin/bash
+# Desc: Aligns path elements with spaces
+# Usage: palign.sh [path]
+#        find . -type f | palign.sh
+# Input: stdin   paths  newline-delimited paths (forward-slash delimited)
+#        psargs  paths  word-delimited paths (forward-slash delimited)
+# Output: stdout
+# Depends: Bash 5.1.16
+# 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
+read_stdin() {
+    # Desc: Consumes stdin; outputs as stdout lines
+    # Input: stdin (consumes)
+    # Output: stdout  (newline delimited)
+    #         return  0  stdin read
+    #         return  1  stdin not present
+    # Example: printf "foo\nbar\n" | read_stdin
+    # Depends: GNU bash (version 5.1.16), GNU Coreutils 8.32 (cat)
+    # Version: 0.1.1
+    # Attrib: Steven Baltakatei Sandoval (2024-01-29). reboil.com
+    local input_stdin output;
+
+    # Store stdin
+    if [[ -p /dev/stdin ]]; then
+        input_stdin="$(cat -)" || {
+            echo "FATAL:Error reading stdin." 1>&2; return 1; };
+    else
+        return 1;
+    fi; 
+    
+    # Store as output array elements
+    ## Read in stdin
+    if [[ -n $input_stdin ]]; then
+        while read -r line; do
+            output+=("$line");
+        done < <(printf "%s\n" "$input_stdin") || {
+            echo "FATAL:Error parsing stdin."; return 1; };
+    fi;
+
+    # Print to stdout
+    printf "%s\n" "${output[@]}";
+
+    return 0;
+}; # read stdin to stdout lines
+read_psarg() {
+    # Desc: Reads arguments; outputs as stdout lines
+    # Input: args
+    # Output: stdout (newline delimited)
+    # Example: read_psarg "$@"
+    # Depends: GNU bash (version 5.1.16)
+    # Version: 0.0.1
+    local input_psarg output;
+    
+    # Store arguments
+    if [[ $# -gt 0 ]]; then
+        input_psarg="$*";
+    fi;
+    
+    # Store as output array elements
+    ## Read in positional arguments
+    if [[ -n $input_psarg ]]; then
+        for arg in "$@"; do
+            output+=("$arg");
+        done;
+    fi;
+
+    # Print to stdout
+    printf "%s\n" "${output[@]}";
+}; # read positional argument to stdout lines
+calculate_max_lengths() {
+    # Calculate maximum lengths of each hierarchical level
+    # Input:   psargs  paths  
+    # Output:  stdout  ints  path element lengths (single line of space-separated ints)
+    local -a paths=("$@");
+    local lengths;
+    
+    for path in "${paths[@]}"; do
+        IFS='/' read -r -a parts <<< "$path";
+        # Analyze path element lengths
+        for i in "${!parts[@]}"; do
+            # Save max path element lengths for previously analyzed paths
+            #   Note: $(( condition ? value_true : value_false ))
+            #     condition:  ${#parts[$i]} > ${lengths[$i]:-0}  is path element longer than previously seen corresponding paths?
+            #     value_true: ${#parts[$i]}                      save new max length
+            #     value_false ${lengths[$i]:-0}                  use previous value (or init with 0 if never seen before)
+            lengths[$i]=$(( ${#parts[$i]} > ${lengths[$i]:-0} ? ${#parts[$i]} : ${lengths[$i]:-0} ));
+        done;
+    done;
+    echo "${lengths[@]}";
+}; # Get maximum lengths of path elements as space-separated ints
+align_paths() {
+    # Align paths
+    # Input:  stdin
+    # Output: stdout
+    # Depends: calculate_max_lengths()
+    local -a paths lengths;
+
+    # Read stdin
+    while read -r line; do
+        paths+=("$line");
+    done < <(read_stdin);
+
+    IFS=" " read -r -a lengths <<< "$(calculate_max_lengths "${paths[@]}")";
+
+    for path in "${paths[@]}"; do
+        IFS='/' read -r -a parts <<< "$path";
+        formatted_path="";
+        for i in "${!parts[@]}"; do
+            if [[ $i -gt 0 ]]; then
+                formatted_path+="/";
+            fi;
+            formatted_path+="${parts[$i]}";
+            if [[ $i -lt $((${#parts[@]} - 1)) ]]; then
+                formatted_path+=$(printf "%*s" $((${lengths[$i]} - ${#parts[$i]} + 1)) "");
+            fi;
+        done;
+        echo "$formatted_path";
+    done;
+}; # Function to format and align paths
+main() {
+    { read_stdin; read_psarg "$@"; } | align_paths;
+}; # Call the function with the file paths
+main "$@";
+
+# Author: Steven Baltakatei Sandoval
+# License; GPLv3+