-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtree-tab
More file actions
executable file
·170 lines (142 loc) · 5.38 KB
/
tree-tab
File metadata and controls
executable file
·170 lines (142 loc) · 5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/bin/bash
showHelp() {
echo "\
OPTIONS:
-D Show debug messages.
-e CMD Evaluate a shell command, replacing '{}' with the filename, and print
its output after ' :: ' for every line. Multiple allowed.
--ignore-path PATTERN
Ignore paths matching the given regular expression
PATTERN (using grep -E, extended Regular Expressions).
-h Show this help.
"
echo ""
echo "Dependencies: tree, grep"
#--limit NUMBER Consider only NUMBER lines. Ignored lines still count toward the total.
}
joinArray() {
local delimiter="$1"
shift 1
local i
local output=
local isFirst=true
for i in "$@"; do
if [[ $isFirst = true ]]; then
isFirst=false
else
output+="$delimiter"
fi
output+="$i"
done
echo -n "$output"
}
generateTree() {
tree "$@" | perl -pe 's/(├── |│ |└── | )/\t/g'
}
evalPerFile() {
local expr="$1"
local skipLineOnError="$2"
local suppressOutput="$3"
local skipChildren="$4"
local numTabs=0
local previousNumTabs line path lineChunks depthDiff pathPopCount lineName tabs lineBeginning escapedPath exprCommand exprOutput exprStatus
local pathChunks=()
# local lineNumber=0
local skippingIndent=
# echo "# WARNING: -e is not fully tested; evaluating per line: '$expr'" # testing is successful so far
# local oIFS="$IFS"
local IFS=$'\n'
while read line; do
# IFS="$oIFS"
if [[ -z $line || $line =~ ^# || $line =~ ^[0-9]+\ directories,\ [0-9]+\ files$ ]]; then
echo "$line" # pass through comments & blank lines
else
if [[ -n $skippingIndent ]]; then
lineBeginning=${line:0:${#skippingIndent}}
if [[ $lineBeginning = $skippingIndent ]]; then
continue
else
skippingIndent= # stop skipping until next match
fi
fi
# if [[ $limit -gt 0 ]]; then
# lineNumber=$((lineNumber + 1)); [[ $lineNumber -gt $limit ]] && break
# fi
previousNumTabs="$numTabs"
# tabs="$(echo -n "$line" | perl -pe 's/^(\t*).*/\1/')"
# lineName="${line:${#tabs}}" # remove leading tabs
lineName="${line#"${line%%[![:space:]]*}"}" # remove leading whitespace # https://stackoverflow.com/a/3352015/278478
# ^' ' ' ' ''' ' | remove from beginning
# ^ ' ' ' ''' ^ | quote?
# ^^' ' ''' | remove from end
# ^^' '^' | invert the inner character class
# ^^^^^^^^^ ' | whitespace character class
# ^ | any non-whitespace characters
tabs="${line/$lineName/}"
numTabs=${#tabs}
depthDiff=$((numTabs - previousNumTabs))
if [[ $depthDiff -lt 1 ]]; then
pathChunks=("${pathChunks[@]:0:$numTabs}")
fi
pathChunks+=("$lineName")
path="$(joinArray '/' "${pathChunks[@]}")"
escapedPath="${path//:/\\:}"
escapedPath="${escapedPath//\$/\\\$}"
exprCommand="$(echo -n "$expr" | perl -pe "s:\{\}:$escapedPath:g")"
exprOutput="$(eval "$exprCommand")"
exprStatus=$?
[[ $debug = true ]] && echo "# expr ($exprCommand) #$exprStatus" >&2
if [[ -n "$skipLineOnError" && $exprStatus != 0 ]]; then
[[ -n $skipChildren ]] && skippingIndent="$tabs"
else
echo -n "$line"
if [[ -z $suppressOutput && -n "$exprOutput" ]]; then
echo -n " :: $exprOutput"
fi
echo "" # newline
fi
[[ $debug = true ]] && echo "# $depthDiff => ${numTabs} | '$path' (esc '$escapedPath')" >&2
fi
# IFS=$'\n'
done
}
inputArgs=("$@")
args=()
debug=false
skipArg=false
followSymlink=false
perFileOutputExpressions=()
perFileFilterExpressions=()
argIndex=-1
# limit=0
for arg in "$@"; do
[[ $debug = true ]] && echo "# arg '$arg'" >&2
argIndex=$((argIndex + 1))
[[ $skipArg = true ]] && { skipArg=false; continue; }
case "$arg" in
'-e') perFileOutputExpressions+=("${inputArgs[$((argIndex + 1))]}"); skipArg=true;;
'--ignore-path') perFileFilterExpressions+=("${inputArgs[$((argIndex + 1))]}"); skipArg=true;;
'-h') showHelp; exit 0;;
'-D') debug=true; echo "# DEBUG: ON" >&2;;
# '--limit') limit="${inputArgs[$((argIndex + 1))]}"; skipArg=true; echo "limit: $limit" >&2;;
*) args+=("$arg");;
esac
done
[[ $debug = true ]] && echo "# tree args: '${args[*]}'" >&2
# [[ $debug = true ]] && echo "# limit: $limit" >&2
generateTreePipeline=("generateTree \"\${args[@]}\"")
concatFilters=
if [[ ${#perFileFilterExpressions[@]} -gt 0 ]]; then
concatFilters="echo -n \"{}\" | grep -vE \"$(joinArray $'\n' "${perFileFilterExpressions[@]}")\""
[[ $debug = true ]] && echo "# DEBUG: concatFilters: $concatFilters" >&2
generateTreePipeline+=("evalPerFile \"\$concatFilters\" skipLineOnError suppressOutput skipChildren") # values 2-4 are not read; written out for clarity
fi
concatPerFileExpressions=
if [[ ${#perFileOutputExpressions[@]} -gt 0 ]]; then
concatPerFileExpressions="$(joinArray "&& echo -n ' :: ';" "${perFileOutputExpressions[@]}")"
[[ $debug = true ]] && echo "# DEBUG: concatPerFileExpressions: $concatPerFileExpressions" >&2
generateTreePipeline+=("evalPerFile \"\$concatPerFileExpressions\"")
fi
generateTreeCommand="$(joinArray " | " "${generateTreePipeline[@]}")"
[[ $debug = true ]] && echo "# DEBUG pipeline: ($generateTreeCommand)" >&2
eval "$generateTreeCommand"