2 # Desc: Aligns path elements with spaces
3 # Usage: palign.sh [path]
4 # find . -type f | palign.sh
5 # Input: stdin paths newline-delimited paths (forward-slash delimited)
6 # psargs paths word-delimited paths (forward-slash delimited)
11 yell
() { echo "$0: $*" >&2; } # print script path and all args to stderr
12 die
() { yell
"$*"; exit 111; } # same as yell() but non-zero exit status
13 must
() { "$@" || die
"cannot $*"; } # runs args as command, reports args if command fails
15 # Desc: Consumes stdin; outputs as stdout lines
16 # Input: stdin (consumes)
17 # Output: stdout (newline delimited)
19 # return 1 stdin not present
20 # Example: printf "foo\nbar\n" | read_stdin
21 # Depends: GNU bash (version 5.1.16), GNU Coreutils 8.32 (cat)
23 # Attrib: Steven Baltakatei Sandoval (2024-01-29). reboil.com
24 local input_stdin output
;
27 if [[ -p /dev
/stdin
]]; then
28 input_stdin
="$(cat -)" ||
{
29 echo "FATAL:Error reading stdin." 1>&2; return 1; };
34 # Store as output array elements
36 if [[ -n $input_stdin ]]; then
37 while read -r line
; do
39 done < <(printf "%s\n" "$input_stdin") ||
{
40 echo "FATAL:Error parsing stdin."; return 1; };
44 printf "%s\n" "${output[@]}";
47 }; # read stdin to stdout lines
49 # Desc: Reads arguments; outputs as stdout lines
51 # Output: stdout (newline delimited)
52 # Example: read_psarg "$@"
53 # Depends: GNU bash (version 5.1.16)
55 local input_psarg output
;
58 if [[ $# -gt 0 ]]; then
62 # Store as output array elements
63 ## Read in positional arguments
64 if [[ -n $input_psarg ]]; then
71 printf "%s\n" "${output[@]}";
72 }; # read positional argument to stdout lines
73 calculate_max_lengths
() {
74 # Calculate maximum lengths of each hierarchical level
76 # Output: stdout ints path element lengths (single line of space-separated ints)
77 local -a paths
=("$@");
80 for path
in "${paths[@]}"; do
81 IFS
='/' read -r -a parts
<<< "$path";
82 # Analyze path element lengths
83 for i
in "${!parts[@]}"; do
84 # Save max path element lengths for previously analyzed paths
85 # Note: $(( condition ? value_true : value_false ))
86 # condition: ${#parts[$i]} > ${lengths[$i]:-0} is path element longer than previously seen corresponding paths?
87 # value_true: ${#parts[$i]} save new max length
88 # value_false ${lengths[$i]:-0} use previous value (or init with 0 if never seen before)
89 lengths
[$i]=$
(( ${#parts[$i]} > ${lengths[$i]:-0} ? ${#parts[$i]} : ${lengths[$i]:-0} ));
93 }; # Get maximum lengths of path elements as space-separated ints
98 # Depends: calculate_max_lengths()
99 local -a paths lengths
;
102 while read -r line
; do
104 done < <(read_stdin
);
106 IFS
=" " read -r -a lengths
<<< "$(calculate_max_lengths "${paths[@]}")";
108 for path
in "${paths[@]}"; do
109 IFS
='/' read -r -a parts
<<< "$path";
111 for i
in "${!parts[@]}"; do
112 if [[ $i -gt 0 ]]; then
115 formatted_path
+="${parts[$i]}";
116 if [[ $i -lt $
((${#parts[@]} - 1)) ]]; then
117 formatted_path
+=$
(printf "%*s" $
((${lengths[$i]} - ${#parts[$i]} + 1)) "");
120 echo "$formatted_path";
122 }; # Function to format and align paths
124 { read_stdin
; read_psarg
"$@"; } | align_paths
;
125 }; # Call the function with the file paths
128 # Author: Steven Baltakatei Sandoval