1 | #!/usr/bin/env bash
|
2 | #
|
3 | # Usage:
|
4 | # demo/xtrace1.sh <function name>
|
5 |
|
6 | #set -o nounset
|
7 | #set -o pipefail
|
8 | #set -o errexit
|
9 |
|
10 | myfunc() {
|
11 | : "myfunc $1"
|
12 | }
|
13 |
|
14 | banner() {
|
15 | echo
|
16 | echo "$@"
|
17 | echo
|
18 | }
|
19 |
|
20 | # bash is the only shell with the "first char repeated" behavior
|
21 | first_char() {
|
22 | for sh in bash dash mksh zsh bin/osh; do
|
23 | echo
|
24 | echo $sh
|
25 | export PS4='$PWD '
|
26 | #export PS4='$ '
|
27 | $sh -x -c 'echo $(echo hi)-'
|
28 | done
|
29 | }
|
30 |
|
31 | # bash repeats the + for command sub, eval, source. Other shells don't.
|
32 | posix() {
|
33 | banner COMMANDSUB
|
34 | set -x
|
35 | foo=$(myfunc commandsub)
|
36 | set +x
|
37 |
|
38 | # Hm this gives you ++
|
39 | banner EVAL
|
40 | set -x
|
41 | eval myfunc evalarg
|
42 | set +x
|
43 |
|
44 | # Also gives you ++
|
45 | banner SOURCE
|
46 | set -x
|
47 | . spec/testdata/source-argv.sh 1 2
|
48 | set +x
|
49 | }
|
50 |
|
51 | # Various stacks:
|
52 | # - proc call stack (similar: FUNCNAME)
|
53 | # - process stack (similar: BASHPID)
|
54 | # - interpreter stack (eval, source. xtrace already respects this)
|
55 | # - and maybe Oil subinterpreters
|
56 |
|
57 | # User level:
|
58 | # - Color
|
59 | # - Indentation
|
60 | # - HTML
|
61 | #
|
62 | # What you really want PARSEABLE traces. Which means each trace item is ONE
|
63 | # LINE. And emitted by a single write() call.
|
64 | #
|
65 | # Related debugging features of OSH:
|
66 | #
|
67 | # - pp cell_ (ASDL), pp proc (QTT)
|
68 | # - osh -n (ASDL)
|
69 | # - Oil expressions: = keyword (ASDL)
|
70 |
|
71 | shopt -s expand_aliases
|
72 | alias e=echo
|
73 |
|
74 | simple() {
|
75 | e alias
|
76 | myfunc invoke
|
77 | ( myfunc subshell )
|
78 | }
|
79 |
|
80 | main() {
|
81 | banner ALIAS
|
82 |
|
83 | set -x
|
84 | e alias
|
85 | set +x
|
86 |
|
87 | banner FUNC
|
88 |
|
89 | set -x
|
90 | myfunc invoke
|
91 | set +x
|
92 |
|
93 | banner SUBSHELL
|
94 | # No increase in +
|
95 | # pid and SHLVL do NOT increase. BASHPID increases.
|
96 | set -x
|
97 | : pid=$$ BASHPID=$BASHPID SHLVL=$SHLVL
|
98 | ( myfunc subshell; : pid=$$ BASHPID=$BASHPID SHLVL=$SHLVL )
|
99 | set +x
|
100 |
|
101 | # Now it changes to ++
|
102 | banner COMMANDSUB
|
103 | set -x
|
104 | foo=$(myfunc commandsub)
|
105 | set +x
|
106 |
|
107 | banner PIPELINE
|
108 | set -x
|
109 | myfunc pipeline | sort
|
110 | set +x
|
111 |
|
112 | banner THREE
|
113 |
|
114 | # Increase to three
|
115 | set -x
|
116 | foo=$(echo $(myfunc commandsub))
|
117 | echo $foo
|
118 | set +x
|
119 |
|
120 | # Hm this gives you ++
|
121 | banner EVAL
|
122 | set -x
|
123 | eval myfunc evalarg
|
124 | set +x
|
125 |
|
126 | # Also gives you ++
|
127 | banner SOURCE
|
128 | set -x
|
129 | source spec/testdata/source-argv.sh 1 2
|
130 | set +x
|
131 |
|
132 | banner RECURSIVE
|
133 | set -x
|
134 | $0 myfunc dollar-zero
|
135 | set +x
|
136 |
|
137 | # TODO: SHELLOPTS not set here?
|
138 | banner "SHELLOPTS=$SHELLOPTS"
|
139 |
|
140 | export SHELLOPTS
|
141 | set -x
|
142 | $0 myfunc dollar-zero-shellopts
|
143 | set +x
|
144 | }
|
145 |
|
146 | main2() {
|
147 | set -x
|
148 |
|
149 | # OK this is useful.
|
150 |
|
151 | # https://unix.stackexchange.com/questions/355965/how-to-check-which-line-of-a-bash-script-is-being-executed
|
152 | PS4='+${LINENO}: '
|
153 |
|
154 | # Test runtime errors like this
|
155 | #PS4='+${LINENO}: $(( 1 / 0 ))'
|
156 |
|
157 | myfunc ps4
|
158 | foo=$(myfunc ps4-commandsub)
|
159 | echo foo
|
160 | }
|
161 |
|
162 | slowfunc() {
|
163 | for i in "$@"; do
|
164 | sleep 0.$i
|
165 | done
|
166 | }
|
167 |
|
168 | concurrency() {
|
169 | set -x
|
170 |
|
171 | # PID prefix would be nice here
|
172 | slowfunc 1 3 | slowfunc 2 4 5 | slowfunc 6
|
173 | }
|
174 |
|
175 | task() {
|
176 | for i in "$@"; do
|
177 | echo $i
|
178 | sleep 0.$i
|
179 | done
|
180 | }
|
181 |
|
182 | through_xargs() {
|
183 | set -x
|
184 | export PS4='+ $$ '
|
185 |
|
186 | # This doesn't work because xargs invokes $0! Not OSH.
|
187 | export OILS_HIJACK_SHEBANG=1
|
188 |
|
189 | # This makes us trace through xargs.
|
190 | #
|
191 | # problem: $0 invokes bash because of the shebang.
|
192 | # We can't use $SHELL $0.
|
193 | # - bash is the only shell that sets $SHELL.
|
194 | # - It's not the right value. "If it is not set when the shell starts, bash
|
195 | # assigns to it the full pathname of the current user's login shell."
|
196 |
|
197 | export SHELLOPTS
|
198 | seq 6 | xargs -n 2 -P 3 -- $0 task
|
199 | }
|
200 |
|
201 | my_ps4() {
|
202 | for i in {1..3}; do
|
203 | echo -n $i
|
204 | done
|
205 | }
|
206 |
|
207 | # The problem with this is you don't want to fork the shell for every line!
|
208 |
|
209 | call_func_in_ps4() {
|
210 | set -x
|
211 | PS4='[$(my-ps4)] '
|
212 | echo one
|
213 | echo two
|
214 | }
|
215 |
|
216 | # EXPANDED argv is displayed, NOT the raw input.
|
217 | # - OK just do assignments?
|
218 |
|
219 | # - bash shows the 'for x in 1 2 3' all on one line
|
220 | # - dash doesn't show the 'for'
|
221 | # - neither does zsh and mksh
|
222 | # - zsh shows line numbers and the function name!
|
223 |
|
224 | # - two statements on one line are broken up
|
225 |
|
226 | # - bash doesn't show 'while'
|
227 |
|
228 | # The $((i+1)) is evaluated. Hm.
|
229 |
|
230 | # Hm we don't implement this, only works at top level
|
231 | # set -v
|
232 |
|
233 | loop() {
|
234 | set -x
|
235 |
|
236 | for x in 1 \
|
237 | 2 \
|
238 | 3; do
|
239 | echo $x; echo =$(echo {x}-)
|
240 | done
|
241 |
|
242 | i=0
|
243 | while test $i -lt 3; do
|
244 | echo $x; echo ${x}-
|
245 | i=$((i+1))
|
246 | if true; then continue; fi
|
247 | done
|
248 | }
|
249 |
|
250 | atoms1() {
|
251 | set -x
|
252 |
|
253 | foo=bar
|
254 |
|
255 | # This messes up a lot of printing.
|
256 | x='one
|
257 | two'
|
258 |
|
259 | i=1
|
260 |
|
261 | [[ -n $x ]]; echo "$x"
|
262 |
|
263 | # $i gets expanded, not i
|
264 | (( y = 42 + i + $i )); echo yo
|
265 |
|
266 | [[ -n $x
|
267 | ]]
|
268 |
|
269 | (( y =
|
270 | 42 +
|
271 | i +
|
272 | $i
|
273 | ))
|
274 | }
|
275 |
|
276 | atoms2() {
|
277 | set -x
|
278 |
|
279 | x='one
|
280 | two'
|
281 |
|
282 | declare -a a
|
283 | a[1]="$x"
|
284 |
|
285 | # This works
|
286 | declare -A A
|
287 | A["$x"]=1
|
288 |
|
289 | a=(1 2 3)
|
290 | A=([k]=v)
|
291 |
|
292 | a=("$x" $x)
|
293 | A=([k]="$x")
|
294 |
|
295 | # Assignment builtins
|
296 |
|
297 | declare -g -r d=0 foo=bar
|
298 | typeset t=1
|
299 | local lo=2
|
300 | export e=3 f=foo
|
301 | readonly r=4
|
302 | }
|
303 |
|
304 | compound() {
|
305 | set -x
|
306 |
|
307 | # Nothing for time
|
308 | time sleep 0
|
309 |
|
310 | # There is no tracing for () and {}
|
311 | { echo b1
|
312 | echo b2
|
313 | }
|
314 |
|
315 | ( echo c1
|
316 | echo c2
|
317 | )
|
318 |
|
319 | # no tracing for if; just the conditions
|
320 | if test -d /; then
|
321 | echo yes
|
322 | else
|
323 | echo no
|
324 | fi
|
325 |
|
326 | # Hm this causes a concurrency problem.
|
327 | # I think we want to buffer the line
|
328 | ls | wc -l | sort
|
329 |
|
330 | # There IS tracing for 'case' line
|
331 | case foo in
|
332 | fo*)
|
333 | echo case
|
334 | ;;
|
335 | *)
|
336 | echo default
|
337 | ;;
|
338 | esac
|
339 |
|
340 | f() {
|
341 | echo hi
|
342 | }
|
343 |
|
344 | }
|
345 |
|
346 | oil_constructs() {
|
347 | echo TODO
|
348 | # BareDecl, VarDecl, PlaceMutation, Expr
|
349 | }
|
350 |
|
351 | details() {
|
352 | PS4='+ ${BASH_SOURCE[0]}:${FUNCNAME[0]}:${LINENO} '
|
353 |
|
354 | # X_pid can have a space after it, or call it ${X_tag}
|
355 | # Or should we could call the whole thing ? ${XTRACE_PREFIX} ?
|
356 | # Idea: $PS5 and $PS6 for push and pop?
|
357 |
|
358 | # Problem: remove the first char behavior? Or only respect it in PS4.
|
359 | #
|
360 | # This string can be OIL_XTRACE_PREFIX='' or something.
|
361 | PS4=' ${X_indent}${X_punct}${X_tag}${BASH_SOURCE[0]}:${FUNCNAME[0]}:${LINENO} '
|
362 |
|
363 |
|
364 | set -x
|
365 |
|
366 | echo hi
|
367 | echo command=$(echo inner)
|
368 | eval 'echo eval'
|
369 | }
|
370 |
|
371 | "$@"
|