| 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 (eval_unsafe_arith) |
| 539 | shopt -s eval_unsafe_arith |
| 540 | |
| 541 | typeset -n ref='a[2]' |
| 542 | a=(zero one two three) |
| 543 | echo ref=$ref |
| 544 | ## STDOUT: |
| 545 | ref=two |
| 546 | ## END |
| 547 | |
| 548 | #### a[expr] in nameref (eval_unsafe_arith) |
| 549 | shopt -s eval_unsafe_arith |
| 550 | |
| 551 | # this confuses code and data |
| 552 | typeset -n ref='a[$(echo 2) + 1]' |
| 553 | a=(zero one two three) |
| 554 | echo ref=$ref |
| 555 | ## STDOUT: |
| 556 | ref=three |
| 557 | ## END |
| 558 | |
| 559 | #### a[@] in nameref (eval_unsafe_arith) |
| 560 | shopt -s eval_unsafe_arith |
| 561 | |
| 562 | # this confuses code and data |
| 563 | typeset -n ref='a[@]' |
| 564 | a=('A B' C) |
| 565 | argv.py ref "$ref" # READ through ref works |
| 566 | ref=(X Y Z) # WRITE through doesn't work |
| 567 | echo status=$? |
| 568 | argv.py 'ref[@]' "${ref[@]}" |
| 569 | argv.py ref "$ref" # JOINING mangles the array? |
| 570 | argv.py 'a[@]' "${a[@]}" |
| 571 | ## STDOUT: |
| 572 | ['ref', 'A B C'] |
| 573 | status=1 |
| 574 | ['ref[@]'] |
| 575 | ['ref', 'A B C'] |
| 576 | ['a[@]', 'A B', 'C'] |
| 577 | ## END |
| 578 | ## OK mksh status: 1 |
| 579 | ## OK mksh stdout-json: "" |
| 580 | |
| 581 | #### mutate through nameref: ref[0]= |
| 582 | |
| 583 | # This is DIFFERENT than the nameref itself being 'array[0]' ! |
| 584 | |
| 585 | array=(X Y Z) |
| 586 | typeset -n ref=array |
| 587 | ref[0]=xx |
| 588 | echo ${array[@]} |
| 589 | ## STDOUT: |
| 590 | xx Y Z |
| 591 | ## END |
| 592 | |
| 593 | #### bad mutation through nameref: ref[0]= where ref is array[0] |
| 594 | array=(X Y Z) |
| 595 | typeset -n ref='array[0]' |
| 596 | ref[0]=foo # error in bash: 'array[0]': not a valid identifier |
| 597 | echo status=$? |
| 598 | echo ${array[@]} |
| 599 | ## STDOUT: |
| 600 | status=1 |
| 601 | X Y Z |
| 602 | ## END |
| 603 | ## BUG mksh STDOUT: |
| 604 | status=0 |
| 605 | foo Y Z |
| 606 | ## END |
| 607 | |
| 608 | #### @ in nameref isn't supported, unlike in ${!ref} |
| 609 | shopt -s eval_unsafe_arith |
| 610 | |
| 611 | set -- A B |
| 612 | typeset -n ref='@' # bash gives an error here |
| 613 | echo status=$? |
| 614 | |
| 615 | echo ref=$ref # bash doesn't give an error here |
| 616 | echo status=$? |
| 617 | ## status: 1 |
| 618 | ## stdout-json: "" |
| 619 | ## OK bash status: 0 |
| 620 | ## OK bash STDOUT: |
| 621 | status=1 |
| 622 | ref= |
| 623 | status=0 |
| 624 | ## END |
| 625 | |
| 626 | #### Unquoted assoc reference on RHS |
| 627 | typeset -A bashup_ev_r |
| 628 | bashup_ev_r['foo']=bar |
| 629 | |
| 630 | p() { |
| 631 | local s=foo |
| 632 | local -n e=bashup_ev["$s"] f=bashup_ev_r["$s"] |
| 633 | # Different! |
| 634 | #local e=bashup_ev["$s"] f=bashup_ev_r["$s"] |
| 635 | argv.py "$f" |
| 636 | } |
| 637 | p |
| 638 | ## STDOUT: |
| 639 | ['bar'] |
| 640 | ## END |
| 641 | ## N-I mksh stdout-json: "" |
| 642 | ## N-I mksh status: 1 |