1 #
2 # Usage:
3 # ./named-ref.test.sh <function name>
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 -n
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
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-json: ""
453 ## BUG bash status: 0
454 ## BUG bash STDOUT:
455 defined
456 ref1=
457 ref2=
458 ## END
459
460 #### Dynamic scope with namerefs
461
462 f3() {
463 local -n ref=$1
464 ref=x
465 }
466
467 f2() {
468 f3 "$@"
469 }
470
471 f1() {
472 local F1=F1
473 echo F1=$F1
474 f2 F1
475 echo F1=$F1
476 }
477 f1
478
479 ## STDOUT:
480 F1=F1
481 F1=x
482 ## END
483
484
485 #### change reference itself
486 x=XX
487 y=YY
488 typeset -n ref=x
489 echo ref=$ref
490 echo x=$x
491 echo y=$y
492
493 echo ----
494 typeset -n ref=y
495 echo ref=$ref
496 echo x=$x
497 echo y=$y
498 echo ----
499 ref=z
500 echo ref=$ref
501 echo x=$x
502 echo y=$y
503
504 ## STDOUT:
505 ref=XX
506 x=XX
507 y=YY
508 ----
509 ref=YY
510 x=XX
511 y=YY
512 ----
513 ref=z
514 x=XX
515 y=z
516 ## END
517
518 #### a[2] in nameref
519 typeset -n ref='a[2]'
520 a=(zero one two three)
521 echo ref=$ref
522 ## STDOUT:
523 ref=two
524 ## END
525
526 #### a[expr] in nameref -- DYNAMIC PARSING, don't want this
527
528 # this confuses code and data
529 typeset -n ref='a[$(echo 2) + 1]'
530 a=(zero one two three)
531 echo ref=$ref
532 ## STDOUT:
533 ref=three
534 ## END
535
536 #### Unquoted assoc reference on RHS
537 typeset -A bashup_ev_r
538 bashup_ev_r['foo']=bar
539
540 p() {
541 local s=foo
542 local -n e=bashup_ev["$s"] f=bashup_ev_r["$s"]
543 # Different!
544 #local e=bashup_ev["$s"] f=bashup_ev_r["$s"]
545 argv.py "$f"
546 }
547 p
548 ## STDOUT:
549 ['bar']
550 ## END
551 ## N-I mksh stdout-json: ""
552 ## N-I mksh status: 1