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 |