1 ## compare_shells: bash zsh mksh ash
2 ## oils_failures_allowed: 2
3
4 #### recursive arith: one level
5 a='b=123'
6 echo $((a))
7 ## stdout: 123
8 ## N-I dash status: 2
9 ## N-I dash stdout-json: ""
10 ## N-I yash stdout: b=123
11
12 #### recursive arith: two levels
13 a='b=c' c='d=123'
14 echo $((a))
15 ## stdout: 123
16 ## N-I dash status: 2
17 ## N-I dash stdout-json: ""
18 ## N-I yash stdout: b=c
19
20 #### recursive arith: short circuit &&, ||
21 # Note: mksh R52 has a bug. Even though it supports a short circuit like
22 # "echo $((cond&&(a=1)))", it doesn't work with "x=a=1; echo
23 # $((cond&&x))". It is fixed at least in mksh R57.
24 # Note: "busybox sh" doesn't support short circuit.
25 a=b=123
26 echo $((1||a)):$((b))
27 echo $((0||a)):$((b))
28 c=d=321
29 echo $((0&&c)):$((d))
30 echo $((1&&c)):$((d))
31 ## STDOUT:
32 1:0
33 1:123
34 0:0
35 1:321
36 ## END
37
38 ## BUG mksh/ash STDOUT:
39 1:123
40 1:123
41 0:321
42 1:321
43 ## END
44
45 ## N-I dash/yash status: 2
46 ## N-I dash/yash STDOUT:
47 1:0
48 ## END
49
50 #### recursive arith: short circuit ?:
51 # Note: "busybox sh" behaves strangely.
52 y=a=123 n=a=321
53 echo $((1?(y):(n))):$((a))
54 echo $((0?(y):(n))):$((a))
55 ## STDOUT:
56 123:123
57 321:321
58 ## END
59 ## BUG ash STDOUT:
60 123:321
61 321:321
62 ## END
63 ## N-I dash status: 2
64 ## N-I dash stdout-json: ""
65 ## N-I yash STDOUT:
66 a=123:0
67 a=321:0
68 ## END
69
70 #### recursive arith: side effects
71 # In Zsh and Busybox sh, the side effect of inner arithmetic
72 # evaluations seems to take effect only after the whole evaluation.
73 a='b=c' c='d=123'
74 echo $((a,d)):$((d))
75 ## stdout: 123:123
76 ## BUG zsh/ash stdout: 0:123
77 ## N-I dash/yash status: 2
78 ## N-I dash/yash stdout-json: ""
79
80 #### recursive arith: recursion
81 loop='i<=100&&(s+=i,i++,loop)' s=0 i=0
82 echo $((a=loop,s))
83 ## stdout: 5050
84 ## N-I mksh status: 1
85 ## N-I mksh stdout-json: ""
86 ## N-I ash/dash/yash status: 2
87 ## N-I ash/dash/yash stdout-json: ""
88
89 #### recursive arith: array elements
90 text[1]='d=123'
91 text[2]='text[1]'
92 text[3]='text[2]'
93 echo $((a=text[3]))
94 ## stdout: 123
95 ## N-I ash/dash/yash status: 2
96 ## N-I ash/dash/yash stdout-json: ""
97
98 #### dynamic arith varname: assign
99 vec2_set () {
100 local this=$1 x=$2 y=$3
101 : $(( ${this}_x = $2 ))
102 : $(( ${this}_y = y ))
103 }
104 vec2_set a 3 4
105 vec2_set b 5 12
106 echo a_x=$a_x a_y=$a_y
107 echo b_x=$b_x b_y=$b_y
108 ## STDOUT:
109 a_x=3 a_y=4
110 b_x=5 b_y=12
111 ## END
112
113 #### dynamic arith varname: read
114
115 vec2_load() {
116 local this=$1
117 x=$(( ${this}_x ))
118 : $(( y = ${this}_y ))
119 }
120 a_x=12 a_y=34
121 vec2_load a
122 echo x=$x y=$y
123 ## STDOUT:
124 x=12 y=34
125 ## END
126
127 #### dynamic arith varname: copy/add
128 shopt -s eval_unsafe_arith # for RHS
129
130 vec2_copy () {
131 local this=$1 rhs=$2
132 : $(( ${this}_x = $(( ${rhs}_x )) ))
133 : $(( ${this}_y = ${rhs}_y ))
134 }
135 vec2_add () {
136 local this=$1 rhs=$2
137 : $(( ${this}_x += $(( ${rhs}_x )) ))
138 : $(( ${this}_y += ${rhs}_y ))
139 }
140 a_x=3 a_y=4
141 b_x=4 b_y=20
142 vec2_copy c a
143 echo c_x=$c_x c_y=$c_y
144 vec2_add c b
145 echo c_x=$c_x c_y=$c_y
146 ## STDOUT:
147 c_x=3 c_y=4
148 c_x=7 c_y=24
149 ## END
150
151 #### is-array with ${var@a}
152 case $SH in (mksh|ash|dash|yash) exit 1 ;; esac
153
154 function ble/is-array { [[ ${!1@a} == *a* ]]; }
155
156 ble/is-array undef
157 echo undef $?
158
159 string=''
160 ble/is-array string
161 echo string $?
162
163 array=(one two three)
164 ble/is-array array
165 echo array $?
166 ## STDOUT:
167 undef 1
168 string 1
169 array 0
170 ## END
171 ## N-I zsh/mksh/ash/dash/yash status: 1
172 ## N-I zsh/mksh/ash/dash/yash stdout-json: ""
173
174
175 #### Sparse array with big index
176
177 # TODO: more InternalStringArray idioms / stress tests ?
178
179 a=()
180
181 if false; then
182 # This takes too long! # From Zulip
183 i=$(( 0x0100000000000000 ))
184 else
185 # smaller number that's OK
186 i=$(( 0x0100000 ))
187 fi
188
189 a[i]=1
190
191 echo len=${#a[@]}
192
193 ## STDOUT:
194 len=1
195 ## END
196
197 ## N-I ash status: 2
198 ## N-I ash STDOUT:
199 ## END
200
201 ## BUG zsh STDOUT:
202 len=1048576
203 ## END
204
205
206 #### shift unshift reverse
207
208 case $SH in mksh|ash) exit ;; esac
209
210 # https://github.com/akinomyoga/ble.sh/blob/79beebd928cf9f6506a687d395fd450d027dc4cd/src/util.sh#L578-L582
211
212 # @fn ble/array#unshift arr value...
213 function ble/array#unshift {
214 builtin eval -- "$1=(\"\${@:2}\" \"\${$1[@]}\")"
215 }
216 # @fn ble/array#shift arr count
217 function ble/array#shift {
218 # Note: Bash 4.3 以下では ${arr[@]:${2:-1}} が offset='${2'
219 # length='-1' に解釈されるので、先に算術式展開させる。
220 builtin eval -- "$1=(\"\${$1[@]:$((${2:-1}))}\")"
221 }
222 # @fn ble/array#reverse arr
223 function ble/array#reverse {
224 builtin eval "
225 set -- \"\${$1[@]}\"; $1=()
226 local e$1 i$1=\$#
227 for e$1; do $1[--i$1]=\"\$e$1\"; done"
228 }
229
230 a=( {1..6} )
231 echo "${a[@]}"
232
233 ble/array#shift a 1
234 echo "${a[@]}"
235
236 ble/array#shift a 2
237 echo "${a[@]}"
238
239 echo ---
240
241 ble/array#unshift a 99
242 echo "${a[@]}"
243
244 echo ---
245
246 # doesn't work in zsh!
247 ble/array#reverse a
248 echo "${a[@]}"
249
250
251 ## STDOUT:
252 1 2 3 4 5 6
253 2 3 4 5 6
254 4 5 6
255 ---
256 99 4 5 6
257 ---
258 6 5 4 99
259 ## END
260
261 ## BUG zsh STDOUT:
262 1 2 3 4 5 6
263 2 3 4 5 6
264 4 5 6
265 ---
266 99 4 5 6
267 ---
268 5 4 99
269 ## END
270
271 ## N-I mksh/ash STDOUT:
272 ## END
273
274
275 #### shopt -u expand_aliases and eval
276 case $SH in zsh|mksh|ash) exit ;; esac
277
278 alias echo=false
279
280 function f {
281 shopt -u expand_aliases
282 eval -- "$1"
283 shopt -s expand_aliases
284 }
285
286 f 'echo hello'
287
288 ## STDOUT:
289 hello
290 ## END
291 ## N-I zsh/mksh/ash STDOUT:
292 ## END
293
294
295 #### Tilde expansions in RHS of designated array initialization
296 case $SH in zsh|mksh|ash) exit ;; esac
297
298 HOME=/home/user
299 declare -A a
300 declare -A a=(['home']=~ ['hello']=~:~:~)
301 echo "${a['home']}"
302 echo "${a['hello']}"
303
304 ## STDOUT:
305 /home/user
306 /home/user:/home/user:/home/user
307 ## END
308
309 # Note: bash-5.2 has a bug that the tilde doesn't expand on the right hand side
310 # of [key]=value. This problem doesn't happen in bash-3.1..5.1 and bash-5.3.
311 ## BUG bash STDOUT:
312 ~
313 ~:~:~
314 ## END
315
316 ## N-I zsh/mksh/ash stdout-json: ""
317
318
319 #### InitializerList (BashArray): index increments with
320 case $SH in zsh|mksh|ash) exit 99;; esac
321 a=([100]=1 2 3 4)
322 printf 'keys: '; argv.py "${!a[@]}"
323 printf 'vals: '; argv.py "${a[@]}"
324 a=([100]=1 2 3 4 [5]=a b c d)
325 printf 'keys: '; argv.py "${!a[@]}"
326 printf 'vals: '; argv.py "${a[@]}"
327 ## STDOUT:
328 keys: ['100', '101', '102', '103']
329 vals: ['1', '2', '3', '4']
330 keys: ['5', '6', '7', '8', '100', '101', '102', '103']
331 vals: ['a', 'b', 'c', 'd', '1', '2', '3', '4']
332 ## END
333 ## N-I zsh/mksh/ash status: 99
334 ## N-I zsh/mksh/ash stdout-json: ""
335
336 #### InitializerList (BashArray): [k]=$v and [k]="$@"
337 case $SH in zsh|mksh|ash) exit 99;; esac
338 i=5
339 v='1 2 3'
340 a=($v [i]=$v)
341 printf 'keys: '; argv.py "${!a[@]}"
342 printf 'vals: '; argv.py "${a[@]}"
343
344 x=(3 5 7)
345 a=($v [i]="${x[*]}")
346 printf 'keys: '; argv.py "${!a[@]}"
347 printf 'vals: '; argv.py "${a[@]}"
348 a=($v [i]="${x[@]}")
349 printf 'keys: '; argv.py "${!a[@]}"
350 printf 'vals: '; argv.py "${a[@]}"
351 a=($v [i]=${x[*]})
352 printf 'keys: '; argv.py "${!a[@]}"
353 printf 'vals: '; argv.py "${a[@]}"
354 a=($v [i]=${x[@]})
355 printf 'keys: '; argv.py "${!a[@]}"
356 printf 'vals: '; argv.py "${a[@]}"
357 ## STDOUT:
358 keys: ['0', '1', '2', '5']
359 vals: ['1', '2', '3', '1 2 3']
360 keys: ['0', '1', '2', '5']
361 vals: ['1', '2', '3', '3 5 7']
362 keys: ['0', '1', '2', '5']
363 vals: ['1', '2', '3', '3 5 7']
364 keys: ['0', '1', '2', '5']
365 vals: ['1', '2', '3', '3 5 7']
366 keys: ['0', '1', '2', '5']
367 vals: ['1', '2', '3', '3 5 7']
368 ## END
369 ## N-I zsh/mksh/ash status: 99
370 ## N-I zsh/mksh/ash stdout-json: ""
371
372
373 #### InitializerList (BashAssoc): [k]=$v and [k]="$@"
374 case $SH in zsh|mksh|ash) exit 99;; esac
375 i=5
376 v='1 2 3'
377 declare -A a
378 a=([i]=$v)
379 printf 'keys: '; argv.py "${!a[@]}"
380 printf 'vals: '; argv.py "${a[@]}"
381
382 x=(3 5 7)
383 a=([i]="${x[*]}")
384 printf 'keys: '; argv.py "${!a[@]}"
385 printf 'vals: '; argv.py "${a[@]}"
386 a=([i]="${x[@]}")
387 printf 'keys: '; argv.py "${!a[@]}"
388 printf 'vals: '; argv.py "${a[@]}"
389 a=([i]=${x[*]})
390 printf 'keys: '; argv.py "${!a[@]}"
391 printf 'vals: '; argv.py "${a[@]}"
392 a=([i]=${x[@]})
393 printf 'keys: '; argv.py "${!a[@]}"
394 printf 'vals: '; argv.py "${a[@]}"
395 ## STDOUT:
396 keys: ['i']
397 vals: ['1 2 3']
398 keys: ['i']
399 vals: ['3 5 7']
400 keys: ['i']
401 vals: ['3 5 7']
402 keys: ['i']
403 vals: ['3 5 7']
404 keys: ['i']
405 vals: ['3 5 7']
406 ## END
407 ## N-I zsh/mksh/ash status: 99
408 ## N-I zsh/mksh/ash stdout-json: ""
409
410 #### InitializerList (BashArray): append to element
411 case $SH in zsh|mksh|ash) exit 99;; esac
412 hello=100
413 a=([hello]=1 [hello]+=2)
414 printf 'keys: '; argv.py "${!a[@]}"
415 printf 'vals: '; argv.py "${a[@]}"
416 a+=([hello]+=:34 [hello]+=:56)
417 printf 'keys: '; argv.py "${!a[@]}"
418 printf 'vals: '; argv.py "${a[@]}"
419 ## STDOUT:
420 keys: ['100']
421 vals: ['12']
422 keys: ['100']
423 vals: ['12:34:56']
424 ## END
425 ## N-I zsh/mksh/ash status: 99
426 ## N-I zsh/mksh/ash stdout-json: ""
427
428 #### InitializerList (BashAssoc): append to element
429 case $SH in zsh|mksh|ash) exit 99;; esac
430 declare -A a
431 hello=100
432 a=([hello]=1 [hello]+=2)
433 printf 'keys: '; argv.py "${!a[@]}"
434 printf 'vals: '; argv.py "${a[@]}"
435 a+=([hello]+=:34 [hello]+=:56)
436 printf 'keys: '; argv.py "${!a[@]}"
437 printf 'vals: '; argv.py "${a[@]}"
438 ## STDOUT:
439 keys: ['hello']
440 vals: ['12']
441 keys: ['hello']
442 vals: ['12:34:56']
443 ## END
444 # Bash >= 5.1 has a bug. Bash <= 5.0 is OK.
445 ## BUG bash STDOUT:
446 keys: ['hello']
447 vals: ['2']
448 keys: ['hello']
449 vals: ['2:34:56']
450 ## END
451 ## N-I zsh/mksh/ash status: 99
452 ## N-I zsh/mksh/ash stdout-json: ""
453
454 #### InitializerList (BashAssoc): non-index forms of element
455 case $SH in zsh|mksh|ash) exit 99;; esac
456 declare -A a
457 a=([j]=1 2 3 4)
458 echo "status=$?"
459 printf 'keys: '; argv.py "${!a[@]}"
460 printf 'vals: '; argv.py "${a[@]}"
461 ## status: 1
462 ## STDOUT:
463 ## END
464 # Bash outputs warning messages and succeeds (exit status 0)
465 ## BUG bash status: 0
466 ## BUG bash STDOUT:
467 status=0
468 keys: ['j']
469 vals: ['1']
470 ## END
471 ## BUG bash STDERR:
472 bash: line 3: a: 2: must use subscript when assigning associative array
473 bash: line 3: a: 3: must use subscript when assigning associative array
474 bash: line 3: a: 4: must use subscript when assigning associative array
475 ## END
476 ## N-I zsh/mksh/ash status: 99
477 ## N-I zsh/mksh/ash stdout-json: ""
478
479
480 #### InitializerList (BashArray): evaluation order (1)
481 # RHS of [k]=v are expanded when the initializer list is instanciated. For the
482 # indexed array, the array indices are evaluated when the array is modified.
483 case $SH in zsh|mksh|ash) exit 99;; esac
484 i=1
485 a=([100+i++]=$((i++)) [200+i++]=$((i++)) [300+i++]=$((i++)))
486 printf 'keys: '; argv.py "${!a[@]}"
487 printf 'vals: '; argv.py "${a[@]}"
488 ## STDOUT:
489 keys: ['104', '205', '306']
490 vals: ['1', '2', '3']
491 ## END
492 ## N-I zsh/mksh/ash status: 99
493 ## N-I zsh/mksh/ash stdout-json: ""
494
495
496 #### InitializerList (BashArray): evaluation order (2)
497 # When evaluating the index, the modification to the array by the previous item
498 # of the initializer list is visible to the current item.
499 case $SH in zsh|mksh|ash) exit 99;; esac
500 a=([0]=1+2+3 [a[0]]=10 [a[6]]=hello)
501 printf 'keys: '; argv.py "${!a[@]}"
502 printf 'vals: '; argv.py "${a[@]}"
503 ## STDOUT:
504 keys: ['0', '6', '10']
505 vals: ['1+2+3', '10', 'hello']
506 ## END
507 ## N-I zsh/mksh/ash status: 99
508 ## N-I zsh/mksh/ash stdout-json: ""
509
510
511 #### InitializerList (BashArray): evaluation order (3)
512 # RHS should be expanded before any modification to the array.
513 case $SH in zsh|mksh|ash) exit 99;; esac
514 a=(old1 old2 old3)
515 a=("${a[2]}" "${a[0]}" "${a[1]}" "${a[2]}" "${a[0]}")
516 printf 'keys: '; argv.py "${!a[@]}"
517 printf 'vals: '; argv.py "${a[@]}"
518 a=(old1 old2 old3)
519 old1=101 old2=102 old3=103
520 new1=201 new2=202 new3=203
521 a+=([0]=new1 [1]=new2 [2]=new3 [5]="${a[2]}" [a[0]]="${a[0]}" [a[1]]="${a[1]}")
522 printf 'keys: '; argv.py "${!a[@]}"
523 printf 'vals: '; argv.py "${a[@]}"
524 ## STDOUT:
525 keys: ['0', '1', '2', '3', '4']
526 vals: ['old3', 'old1', 'old2', 'old3', 'old1']
527 keys: ['0', '1', '2', '5', '201', '202']
528 vals: ['new1', 'new2', 'new3', 'old3', 'old1', 'old2']
529 ## END
530 ## N-I zsh/mksh/ash status: 99
531 ## N-I zsh/mksh/ash stdout-json: ""
532
533
534 #### Issue #1069 [57] - Variable v should be visible after IFS= eval 'local v=...'
535
536 set -u
537
538 f() {
539 # The temp env messes it up
540 IFS= eval "local v=\"\$*\""
541
542 # Bug does not appear with only eval
543 # eval "local v=\"\$*\""
544
545 #declare -p v
546 echo v=$v
547
548 # test -v v; echo "v defined $?"
549 }
550
551 f h e l l o
552
553 ## STDOUT:
554 v=hello
555 ## END
556
557
558 #### Issue #1069 [59] - Assigning Str to BashArray/BashAssoc should not remove BashArray/BashAssoc
559 case $SH in zsh|ash) exit ;; esac
560
561 a=(1 2 3)
562 a=99
563 typeset -p a
564
565 typeset -A A=([k]=v)
566 A=99
567 typeset -p A
568
569 ## STDOUT:
570 declare -a a=([0]="99" [1]="2" [2]="3")
571 declare -A A=([0]="99" [k]="v" )
572 ## END
573
574 ## OK mksh status: 1
575 ## OK mksh STDOUT:
576 set -A a
577 typeset a[0]=99
578 typeset a[1]=2
579 typeset a[2]=3
580 ## END
581
582 ## N-I zsh/ash STDOUT:
583 ## END
584
585 #### Issue #1069 [53] - LHS array parsing a[1 + 2]=3 (see spec/array-assign for more)
586 case $SH in zsh|ash) exit ;; esac
587
588 a[1 + 2]=7
589 a[3|4]=8
590 a[(1+2)*3]=9
591
592 typeset -p a
593
594 # Dynamic parsing
595 expr='1 + 2'
596 a[expr]=55
597
598 b=(42)
599 expr='b[0]'
600 a[3 + $expr - 4]=66
601
602 typeset -p a
603
604 ## STDOUT:
605 declare -a a=([3]="7" [7]="8" [9]="9")
606 declare -a a=([3]="55" [7]="8" [9]="9" [41]="66")
607 ## END
608
609 ## OK mksh STDOUT:
610 set -A a
611 typeset a[3]=7
612 typeset a[7]=8
613 typeset a[9]=9
614 set -A a
615 typeset a[3]=55
616 typeset a[7]=8
617 typeset a[9]=9
618 typeset a[41]=66
619 ## END
620
621 ## N-I zsh/ash STDOUT:
622 ## END
623