1 | # Run with
|
2 | #
|
3 | # $ test/spec.sh nameref
|
4 |
|
5 | #### pass array by reference
|
6 | show_value() {
|
7 | local -n array_name=$1
|
8 | local idx=$2
|
9 | echo "${array_name[$idx]}"
|
10 | }
|
11 | shadock=(ga bu zo meu)
|
12 | show_value shadock 2
|
13 | ## stdout: zo
|
14 |
|
15 | #### mutate array by reference
|
16 | set1() {
|
17 | local -n array_name=$1
|
18 | local val=$2
|
19 | array_name[1]=$val
|
20 | }
|
21 | shadock=(a b c d)
|
22 | set1 shadock ZZZ
|
23 | echo ${shadock[@]}
|
24 | ## STDOUT:
|
25 | a ZZZ c d
|
26 | ## END
|
27 |
|
28 | #### pass assoc array by reference
|
29 | show_value() {
|
30 | local -n array_name=$1
|
31 | local idx=$2
|
32 | echo "${array_name[$idx]}"
|
33 | }
|
34 | days=([monday]=eggs [tuesday]=bread [sunday]=jam)
|
35 | show_value days sunday
|
36 | ## stdout: jam
|
37 | ## BUG mksh stdout: [monday]=eggs
|
38 | # mksh note: it coerces "days" to 0? Horrible.
|
39 |
|
40 | #### pass local array by reference, relying on DYNAMIC SCOPING
|
41 | show_value() {
|
42 | local -n array_name=$1
|
43 | local idx=$2
|
44 | echo "${array_name[$idx]}"
|
45 | }
|
46 | caller() {
|
47 | local shadock=(ga bu zo meu)
|
48 | show_value shadock 2
|
49 | }
|
50 | caller
|
51 | ## stdout: zo
|
52 | # mksh appears not to have local arrays!
|
53 | ## BUG mksh stdout-json: ""
|
54 | ## BUG mksh status: 1
|
55 |
|
56 |
|
57 | #### flag -n and +n
|
58 | x=foo
|
59 |
|
60 | ref=x
|
61 |
|
62 | echo ref=$ref
|
63 |
|
64 | typeset -n ref
|
65 | echo ref=$ref
|
66 |
|
67 | # mutate underlying var
|
68 | x=bar
|
69 | echo ref=$ref
|
70 |
|
71 | typeset +n ref
|
72 | echo ref=$ref
|
73 |
|
74 | ## STDOUT:
|
75 | ref=x
|
76 | ref=foo
|
77 | ref=bar
|
78 | ref=x
|
79 | ## END
|
80 |
|
81 | #### mutating through nameref: ref=
|
82 | x=XX
|
83 | y=YY
|
84 |
|
85 | ref=x
|
86 | ref=y
|
87 | echo 1 ref=$ref
|
88 |
|
89 | # now it's a reference
|
90 | typeset -n ref
|
91 |
|
92 | echo 2 ref=$ref # prints YY
|
93 |
|
94 | ref=XXXX
|
95 | echo 3 ref=$ref # it actually prints y, which is XXXX
|
96 |
|
97 | # now Y is mutated!
|
98 | echo 4 y=$y
|
99 |
|
100 | ## STDOUT:
|
101 | 1 ref=y
|
102 | 2 ref=YY
|
103 | 3 ref=XXXX
|
104 | 4 y=XXXX
|
105 | ## END
|
106 |
|
107 |
|
108 | #### flag -n combined ${!ref} -- bash INVERTS
|
109 | foo=FOO # should NOT use this
|
110 |
|
111 | x=foo
|
112 | ref=x
|
113 |
|
114 | echo ref=$ref
|
115 | echo "!ref=${!ref}"
|
116 |
|
117 | echo 'NOW A NAMEREF'
|
118 |
|
119 | typeset -n ref
|
120 | echo ref=$ref
|
121 | echo "!ref=${!ref}"
|
122 |
|
123 | ## STDOUT:
|
124 | ref=x
|
125 | !ref=foo
|
126 | NOW A NAMEREF
|
127 | ref=foo
|
128 | !ref=x
|
129 | ## END
|
130 | ## N-I mksh STDOUT:
|
131 | ref=x
|
132 | !ref=ref
|
133 | NOW A NAMEREF
|
134 | ref=foo
|
135 | !ref=x
|
136 | ## END
|
137 |
|
138 | #### named ref with $# doesn't work
|
139 | set -- one two three
|
140 |
|
141 | ref='#'
|
142 | echo ref=$ref
|
143 | typeset -n ref
|
144 | echo ref=$ref
|
145 |
|
146 | ## STDOUT:
|
147 | ref=#
|
148 | ref=#
|
149 | ## END
|
150 |
|
151 | # mksh does respect it!! Gah.
|
152 | ## OK mksh STDOUT:
|
153 | ref=#
|
154 | ref=3
|
155 | ## END
|
156 |
|
157 |
|
158 | #### named ref with $# and shopt -s strict_nameref
|
159 | shopt -s strict_nameref
|
160 |
|
161 | ref='#'
|
162 | echo ref=$ref
|
163 | typeset -n ref
|
164 | echo ref=$ref
|
165 | ## STDOUT:
|
166 | ref=#
|
167 | ## END
|
168 | ## status: 1
|
169 | ## N-I bash status: 0
|
170 | ## N-I bash STDOUT:
|
171 | ref=#
|
172 | ref=#
|
173 | ## END
|
174 | ## N-I mksh status: 0
|
175 | ## N-I mksh STDOUT:
|
176 | ref=#
|
177 | ref=0
|
178 | ## END
|
179 |
|
180 | #### named ref with 1 $1 etc.
|
181 | set -- one two three
|
182 |
|
183 | x=X
|
184 |
|
185 | ref='1'
|
186 | echo ref=$ref
|
187 | typeset -n ref
|
188 | echo ref=$ref
|
189 |
|
190 | # BUG: This is really assigning '1', which is INVALID
|
191 | # with strict_nameref that degrades!!!
|
192 | ref2='$1'
|
193 | echo ref2=$ref2
|
194 | typeset -n ref2
|
195 | echo ref2=$ref2
|
196 |
|
197 | x=foo
|
198 |
|
199 | ref3='x'
|
200 | echo ref3=$ref3
|
201 | typeset -n ref3
|
202 | echo ref3=$ref3
|
203 |
|
204 | ## STDOUT:
|
205 | ref=1
|
206 | ref=1
|
207 | ref2=$1
|
208 | ref2=$1
|
209 | ref3=x
|
210 | ref3=foo
|
211 | ## END
|
212 | ## BUG mksh status: 1
|
213 | ## BUG mksh STDOUT:
|
214 | ref=1
|
215 | ref=one
|
216 | ref2=$1
|
217 | ## END
|
218 |
|
219 | #### assign to invalid ref
|
220 | ref=1 # mksh makes this READ-ONLY! Because it's not valid.
|
221 |
|
222 | echo ref=$ref
|
223 | typeset -n ref
|
224 | echo ref=$ref
|
225 |
|
226 | ref=foo
|
227 | echo ref=$ref
|
228 | ## STDOUT:
|
229 | ref=1
|
230 | ref=1
|
231 | ref=foo
|
232 | ## END
|
233 | ## OK mksh status: 2
|
234 | ## OK mksh STDOUT:
|
235 | ref=1
|
236 | ref=
|
237 | ## END
|
238 |
|
239 | #### assign to invalid ref with strict_nameref
|
240 | case $SH in *bash|*mksh) exit ;; esac
|
241 |
|
242 | shopt -s strict_nameref
|
243 |
|
244 | ref=1
|
245 |
|
246 | echo ref=$ref
|
247 | typeset -n ref
|
248 | echo ref=$ref
|
249 |
|
250 | ref=foo
|
251 | echo ref=$ref
|
252 | ## status: 1
|
253 | ## STDOUT:
|
254 | ref=1
|
255 | ## END
|
256 | ## N-I bash/mksh status: 0
|
257 | ## N-I bash/mksh stdout-json: ""
|
258 |
|
259 | #### name ref on Undef cell
|
260 | typeset -n ref
|
261 |
|
262 | # This is technically incorrect: an undefined name shouldn't evaluate to empty
|
263 | # string. mksh doesn't allow it.
|
264 | echo ref=$ref
|
265 |
|
266 | echo nounset
|
267 | set -o nounset
|
268 | echo ref=$ref
|
269 | ## status: 1
|
270 | ## STDOUT:
|
271 | ref=
|
272 | nounset
|
273 | ## END
|
274 | ## OK mksh stdout-json: ""
|
275 |
|
276 | #### assign to empty nameref and invalid nameref
|
277 | typeset -n ref
|
278 | echo ref=$ref
|
279 |
|
280 | # this is a no-op in bash, should be stricter
|
281 | ref=x
|
282 | echo ref=$ref
|
283 |
|
284 | typeset -n ref2=undef
|
285 | echo ref2=$ref2
|
286 | ref2=x
|
287 | echo ref2=$ref2
|
288 |
|
289 | ## STDOUT:
|
290 | ref=
|
291 | ref=
|
292 | ref2=
|
293 | ref2=x
|
294 | ## END
|
295 |
|
296 | # mksh gives a good error: empty nameref target
|
297 | ## OK mksh status: 1
|
298 | ## OK mksh stdout-json: ""
|
299 |
|
300 | #### -n attribute before it has a value
|
301 | typeset -n ref
|
302 |
|
303 | echo ref=$ref
|
304 |
|
305 | # Now that it's a string, it still has the -n attribute
|
306 | x=XX
|
307 | ref=x
|
308 | echo ref=$ref
|
309 |
|
310 | ## STDOUT:
|
311 | ref=
|
312 | ref=XX
|
313 | ## END
|
314 | ## N-I mksh status: 1
|
315 | ## N-I mksh stdout-json: ""
|
316 |
|
317 | #### -n attribute on array is hard error, not a warning
|
318 | x=X
|
319 | typeset -n ref #=x
|
320 | echo hi
|
321 |
|
322 | # bash prints warning: REMOVES the nameref attribute here!
|
323 | ref=(x y)
|
324 | echo ref=$ref
|
325 |
|
326 | ## status: 1
|
327 | ## STDOUT:
|
328 | hi
|
329 | ## END
|
330 | ## N-I mksh status: 1
|
331 | ## N-I mksh stdout-json: ""
|
332 | ## BUG bash status: 0
|
333 | ## BUG bash STDOUT:
|
334 | hi
|
335 | ref=x
|
336 | ## END
|
337 |
|
338 | #### exported nameref
|
339 | x=foo
|
340 | typeset -n -x ref=x
|
341 |
|
342 | # hm bash ignores it but mksh doesn't. maybe disallow it.
|
343 | printenv.py x ref
|
344 | echo ---
|
345 | export x
|
346 | printenv.py x ref
|
347 | ## STDOUT:
|
348 | None
|
349 | x
|
350 | ---
|
351 | foo
|
352 | x
|
353 | ## END
|
354 | ## OK mksh STDOUT:
|
355 | None
|
356 | None
|
357 | ---
|
358 | foo
|
359 | None
|
360 | ## END
|
361 |
|
362 |
|
363 | #### readonly nameref doesn't prevent assigning through it
|
364 |
|
365 | # hm bash also ignores -r when -n is set
|
366 |
|
367 | x=XX
|
368 | typeset -n -r ref=x
|
369 |
|
370 | echo ref=$ref
|
371 |
|
372 | # it feels like I shouldn't be able to mutate this?
|
373 | ref=XXXX
|
374 | echo ref=$ref
|
375 |
|
376 | x=X
|
377 | echo x=$x
|
378 |
|
379 | ## STDOUT:
|
380 | ref=XX
|
381 | ref=XXXX
|
382 | x=X
|
383 | ## END
|
384 |
|
385 | #### readonly var can't be assigned through nameref
|
386 |
|
387 | x=X
|
388 | typeset -n -r ref=x
|
389 |
|
390 | echo ref=$ref
|
391 |
|
392 | # it feels like I shouldn't be able to mutate this?
|
393 | ref=XX
|
394 | echo ref=$ref
|
395 |
|
396 | # now the underling variable is immutable
|
397 | typeset -r x
|
398 |
|
399 | ref=XXX
|
400 | echo ref=$ref
|
401 | echo x=$x
|
402 |
|
403 | ## status: 1
|
404 | ## OK mksh status: 2
|
405 | ## STDOUT:
|
406 | ref=X
|
407 | ref=XX
|
408 | ## END
|
409 |
|
410 | ## OK bash status: 0
|
411 | ## OK bash STDOUT:
|
412 | ref=X
|
413 | ref=XX
|
414 | ref=XX
|
415 | x=XX
|
416 | ## END
|
417 |
|
418 | #### unset nameref
|
419 | x=X
|
420 | typeset -n ref=x
|
421 | echo ref=$ref
|
422 |
|
423 | # this works
|
424 | unset ref
|
425 | echo ref=$ref
|
426 | echo x=$x
|
427 |
|
428 | ## STDOUT:
|
429 | ref=X
|
430 | ref=
|
431 | x=
|
432 | ## END
|
433 |
|
434 | #### Chain of namerefs
|
435 | x=foo
|
436 | typeset -n ref=x
|
437 | typeset -n ref_to_ref=ref
|
438 | echo ref_to_ref=$ref_to_ref
|
439 | echo ref=$ref
|
440 | ## STDOUT:
|
441 | ref_to_ref=foo
|
442 | ref=foo
|
443 | ## END
|
444 |
|
445 | #### Mutually recursive namerefs detected on READ
|
446 | typeset -n ref1=ref2
|
447 | typeset -n ref2=ref1
|
448 | echo defined
|
449 | echo ref1=$ref1
|
450 | echo ref2=$ref1
|
451 | ## status: 1
|
452 | ## STDOUT:
|
453 | defined
|
454 | ## END
|
455 | ## OK mksh stdout-json: ""
|
456 | ## BUG bash status: 0
|
457 | ## BUG bash STDOUT:
|
458 | defined
|
459 | ref1=
|
460 | ref2=
|
461 | ## END
|
462 |
|
463 | #### Mutually recursive namerefs detected on WRITE
|
464 | typeset -n ref1=ref2
|
465 | typeset -n ref2=ref1 # not detected here
|
466 | echo defined $?
|
467 | ref1=z # detected here
|
468 | echo mutated $?
|
469 | ## status: 1
|
470 | ## STDOUT:
|
471 | defined 0
|
472 | ## END
|
473 | ## OK mksh stdout-json: ""
|
474 | ## BUG bash status: 0
|
475 | ## BUG bash STDOUT:
|
476 | defined 0
|
477 | mutated 1
|
478 | ## END
|
479 |
|
480 | #### Dynamic scope with namerefs
|
481 |
|
482 | f3() {
|
483 | local -n ref=$1
|
484 | ref=x
|
485 | }
|
486 |
|
487 | f2() {
|
488 | f3 "$@"
|
489 | }
|
490 |
|
491 | f1() {
|
492 | local F1=F1
|
493 | echo F1=$F1
|
494 | f2 F1
|
495 | echo F1=$F1
|
496 | }
|
497 | f1
|
498 |
|
499 | ## STDOUT:
|
500 | F1=F1
|
501 | F1=x
|
502 | ## END
|
503 |
|
504 |
|
505 | #### change reference itself
|
506 | x=XX
|
507 | y=YY
|
508 | typeset -n ref=x
|
509 | echo ref=$ref
|
510 | echo x=$x
|
511 | echo y=$y
|
512 |
|
513 | echo ----
|
514 | typeset -n ref=y
|
515 | echo ref=$ref
|
516 | echo x=$x
|
517 | echo y=$y
|
518 | echo ----
|
519 | ref=z
|
520 | echo ref=$ref
|
521 | echo x=$x
|
522 | echo y=$y
|
523 |
|
524 | ## STDOUT:
|
525 | ref=XX
|
526 | x=XX
|
527 | y=YY
|
528 | ----
|
529 | ref=YY
|
530 | x=XX
|
531 | y=YY
|
532 | ----
|
533 | ref=z
|
534 | x=XX
|
535 | y=z
|
536 | ## END
|
537 |
|
538 | #### a[2] in nameref
|
539 |
|
540 | typeset -n ref='a[2]'
|
541 | a=(zero one two three)
|
542 | echo ref=$ref
|
543 | ## STDOUT:
|
544 | ref=two
|
545 | ## END
|
546 |
|
547 | #### a[expr] in nameref
|
548 |
|
549 | # this confuses code and data
|
550 | typeset -n ref='a[$(echo 2) + 1]'
|
551 | a=(zero one two three)
|
552 | echo ref=$ref
|
553 | ## STDOUT:
|
554 | ref=three
|
555 | ## END
|
556 |
|
557 | #### a[@] in nameref
|
558 |
|
559 | # this confuses code and data
|
560 | typeset -n ref='a[@]'
|
561 | a=('A B' C)
|
562 | argv.py ref "$ref" # READ through ref works
|
563 | ref=(X Y Z) # WRITE through doesn't work
|
564 | echo status=$?
|
565 | argv.py 'ref[@]' "${ref[@]}"
|
566 | argv.py ref "$ref" # JOINING mangles the array?
|
567 | argv.py 'a[@]' "${a[@]}"
|
568 | ## STDOUT:
|
569 | ['ref', 'A B C']
|
570 | status=1
|
571 | ['ref[@]']
|
572 | ['ref', 'A B C']
|
573 | ['a[@]', 'A B', 'C']
|
574 | ## END
|
575 | ## OK mksh status: 1
|
576 | ## OK mksh stdout-json: ""
|
577 |
|
578 | #### mutate through nameref: ref[0]=
|
579 |
|
580 | # This is DIFFERENT than the nameref itself being 'array[0]' !
|
581 |
|
582 | array=(X Y Z)
|
583 | typeset -n ref=array
|
584 | ref[0]=xx
|
585 | echo ${array[@]}
|
586 | ## STDOUT:
|
587 | xx Y Z
|
588 | ## END
|
589 |
|
590 | #### bad mutation through nameref: ref[0]= where ref is array[0]
|
591 | array=(X Y Z)
|
592 | typeset -n ref='array[0]'
|
593 | ref[0]=foo # error in bash: 'array[0]': not a valid identifier
|
594 | echo status=$?
|
595 | echo ${array[@]}
|
596 | ## STDOUT:
|
597 | status=1
|
598 | X Y Z
|
599 | ## END
|
600 | ## BUG mksh STDOUT:
|
601 | status=0
|
602 | foo Y Z
|
603 | ## END
|
604 |
|
605 | #### @ in nameref isn't supported, unlike in ${!ref}
|
606 |
|
607 | set -- A B
|
608 | typeset -n ref='@' # bash gives an error here
|
609 | echo status=$?
|
610 |
|
611 | echo ref=$ref # bash doesn't give an error here
|
612 | echo status=$?
|
613 | ## status: 1
|
614 | ## stdout-json: ""
|
615 | ## OK bash status: 0
|
616 | ## OK bash STDOUT:
|
617 | status=1
|
618 | ref=
|
619 | status=0
|
620 | ## END
|
621 |
|
622 | #### Unquoted assoc reference on RHS
|
623 | typeset -A bashup_ev_r
|
624 | bashup_ev_r['foo']=bar
|
625 |
|
626 | p() {
|
627 | local s=foo
|
628 | local -n e=bashup_ev["$s"] f=bashup_ev_r["$s"]
|
629 | # Different!
|
630 | #local e=bashup_ev["$s"] f=bashup_ev_r["$s"]
|
631 | argv.py "$f"
|
632 | }
|
633 | p
|
634 | ## STDOUT:
|
635 | ['bar']
|
636 | ## END
|
637 | ## N-I mksh stdout-json: ""
|
638 | ## N-I mksh status: 1
|