Commit | Line | Data |
---|---|---|
6ad3cfe2 SBS |
1 | #!/bin/bash |
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) | |
7 | # Output: stdout | |
8 | # Depends: Bash 5.1.16 | |
9 | # Version: 0.0.1 | |
10 | ||
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 | |
14 | read_stdin() { | |
15 | # Desc: Consumes stdin; outputs as stdout lines | |
16 | # Input: stdin (consumes) | |
17 | # Output: stdout (newline delimited) | |
18 | # return 0 stdin read | |
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) | |
22 | # Version: 0.1.1 | |
23 | # Attrib: Steven Baltakatei Sandoval (2024-01-29). reboil.com | |
24 | local input_stdin output; | |
25 | ||
26 | # Store stdin | |
27 | if [[ -p /dev/stdin ]]; then | |
28 | input_stdin="$(cat -)" || { | |
29 | echo "FATAL:Error reading stdin." 1>&2; return 1; }; | |
30 | else | |
31 | return 1; | |
32 | fi; | |
33 | ||
34 | # Store as output array elements | |
35 | ## Read in stdin | |
36 | if [[ -n $input_stdin ]]; then | |
37 | while read -r line; do | |
38 | output+=("$line"); | |
39 | done < <(printf "%s\n" "$input_stdin") || { | |
40 | echo "FATAL:Error parsing stdin."; return 1; }; | |
41 | fi; | |
42 | ||
43 | # Print to stdout | |
44 | printf "%s\n" "${output[@]}"; | |
45 | ||
46 | return 0; | |
47 | }; # read stdin to stdout lines | |
48 | read_psarg() { | |
49 | # Desc: Reads arguments; outputs as stdout lines | |
50 | # Input: args | |
51 | # Output: stdout (newline delimited) | |
52 | # Example: read_psarg "$@" | |
53 | # Depends: GNU bash (version 5.1.16) | |
54 | # Version: 0.0.1 | |
55 | local input_psarg output; | |
56 | ||
57 | # Store arguments | |
58 | if [[ $# -gt 0 ]]; then | |
59 | input_psarg="$*"; | |
60 | fi; | |
61 | ||
62 | # Store as output array elements | |
63 | ## Read in positional arguments | |
64 | if [[ -n $input_psarg ]]; then | |
65 | for arg in "$@"; do | |
66 | output+=("$arg"); | |
67 | done; | |
68 | fi; | |
69 | ||
70 | # Print to stdout | |
71 | printf "%s\n" "${output[@]}"; | |
72 | }; # read positional argument to stdout lines | |
73 | calculate_max_lengths() { | |
74 | # Calculate maximum lengths of each hierarchical level | |
75 | # Input: psargs paths | |
76 | # Output: stdout ints path element lengths (single line of space-separated ints) | |
77 | local -a paths=("$@"); | |
78 | local lengths; | |
79 | ||
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} )); | |
90 | done; | |
91 | done; | |
92 | echo "${lengths[@]}"; | |
93 | }; # Get maximum lengths of path elements as space-separated ints | |
94 | align_paths() { | |
95 | # Align paths | |
96 | # Input: stdin | |
97 | # Output: stdout | |
98 | # Depends: calculate_max_lengths() | |
99 | local -a paths lengths; | |
100 | ||
101 | # Read stdin | |
102 | while read -r line; do | |
103 | paths+=("$line"); | |
104 | done < <(read_stdin); | |
105 | ||
106 | IFS=" " read -r -a lengths <<< "$(calculate_max_lengths "${paths[@]}")"; | |
107 | ||
108 | for path in "${paths[@]}"; do | |
109 | IFS='/' read -r -a parts <<< "$path"; | |
110 | formatted_path=""; | |
111 | for i in "${!parts[@]}"; do | |
112 | if [[ $i -gt 0 ]]; then | |
113 | formatted_path+="/"; | |
114 | fi; | |
115 | formatted_path+="${parts[$i]}"; | |
116 | if [[ $i -lt $((${#parts[@]} - 1)) ]]; then | |
117 | formatted_path+=$(printf "%*s" $((${lengths[$i]} - ${#parts[$i]} + 1)) ""); | |
118 | fi; | |
119 | done; | |
120 | echo "$formatted_path"; | |
121 | done; | |
122 | }; # Function to format and align paths | |
123 | main() { | |
124 | { read_stdin; read_psarg "$@"; } | align_paths; | |
125 | }; # Call the function with the file paths | |
126 | main "$@"; | |
127 | ||
128 | # Author: Steven Baltakatei Sandoval | |
129 | # License; GPLv3+ |