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