1 ## compare_shells: bash dash mksh zsh
2
3 #### Lazy Evaluation of Alternative
4 i=0
5 x=x
6 echo ${x:-$((i++))}
7 echo $i
8 echo ${undefined:-$((i++))}
9 echo $i # i is one because the alternative was only evaluated once
10 ## status: 0
11 ## stdout-json: "x\n0\n0\n1\n"
12 ## N-I dash status: 2
13 ## N-I dash stdout-json: "x\n0\n"
14
15 #### Default value when empty
16 empty=''
17 echo ${empty:-is empty}
18 ## stdout: is empty
19
20 #### Default value when unset
21 echo ${unset-is unset}
22 ## stdout: is unset
23
24 #### Unquoted with array as default value
25 set -- '1 2' '3 4'
26 argv.py X${unset=x"$@"x}X
27 argv.py X${unset=x$@x}X # If you want OSH to split, write this
28 # osh
29 ## STDOUT:
30 ['Xx1', '2', '3', '4xX']
31 ['Xx1', '2', '3', '4xX']
32 ## END
33 ## OK osh STDOUT:
34 ['Xx1 2', '3 4xX']
35 ['Xx1', '2', '3', '4xX']
36 ## END
37 ## OK zsh STDOUT:
38 ['Xx1 2 3 4xX']
39 ['Xx1 2 3 4xX']
40 ## END
41
42 #### Quoted with array as default value
43 set -- '1 2' '3 4'
44 argv.py "X${unset=x"$@"x}X"
45 argv.py "X${unset=x$@x}X" # OSH is the same here
46 ## STDOUT:
47 ['Xx1 2 3 4xX']
48 ['Xx1 2 3 4xX']
49 ## END
50 ## OK osh STDOUT:
51 ['Xx1 2', '3 4xX']
52 ['Xx1 2 3 4xX']
53 ## END
54
55 #### Assign default with array
56 set -- '1 2' '3 4'
57 argv.py X${unset=x"$@"x}X
58 argv.py "$unset"
59 ## STDOUT:
60 ['Xx1', '2', '3', '4xX']
61 ['x1 2 3 4x']
62 ## END
63 ## OK osh STDOUT:
64 ['Xx1 2', '3 4xX']
65 ['x1 2 3 4x']
66 ## END
67 ## OK zsh STDOUT:
68 ['Xx1 2 3 4xX']
69 ['x1 2 3 4x']
70 ## END
71
72 #### Assign default value when empty
73 empty=''
74 ${empty:=is empty}
75 echo $empty
76 ## stdout: is empty
77
78 #### Assign default value when unset
79 ${unset=is unset}
80 echo $unset
81 ## stdout: is unset
82
83 #### ${v:+foo} Alternative value when empty
84 v=foo
85 empty=''
86 echo ${v:+v is not empty} ${empty:+is not empty}
87 ## stdout: v is not empty
88
89 #### ${v+foo} Alternative value when unset
90 v=foo
91 echo ${v+v is not unset} ${unset:+is not unset}
92 ## stdout: v is not unset
93
94 #### "${x+foo}" quoted (regression)
95 # Python's configure caught this
96 argv.py "${with_icc+set}" = set
97 ## STDOUT:
98 ['', '=', 'set']
99 ## END
100
101 #### ${s+foo} and ${s:+foo} when set -u
102 set -u
103 v=v
104 echo v=${v:+foo}
105 echo v=${v+foo}
106 unset v
107 echo v=${v:+foo}
108 echo v=${v+foo}
109 ## STDOUT:
110 v=foo
111 v=foo
112 v=
113 v=
114 ## END
115
116 #### "${array[@]} with set -u (bash is outlier)
117 case $SH in dash) exit ;; esac
118
119 set -u
120
121 typeset -a empty
122 empty=()
123
124 echo empty /"${empty[@]}"/
125 echo undefined /"${undefined[@]}"/
126
127 ## status: 1
128 ## STDOUT:
129 empty //
130 ## END
131
132 ## BUG bash status: 0
133 ## BUG bash STDOUT:
134 empty //
135 undefined //
136 ## END
137
138 # empty array is unset in mksh
139 ## BUG mksh status: 1
140 ## BUG mksh STDOUT:
141 ## END
142
143 ## N-I dash status: 0
144 ## N-I dash STDOUT:
145 ## END
146
147
148 #### "${undefined[@]+foo}" and "${undefined[@]:+foo}", with set -u
149 case $SH in dash) exit ;; esac
150
151 set -u
152
153 echo plus /"${array[@]+foo}"/
154 echo plus colon /"${array[@]:+foo}"/
155
156 ## STDOUT:
157 plus //
158 plus colon //
159 ## END
160
161 ## N-I dash STDOUT:
162 ## END
163
164 #### "${a[@]+foo}" and "${a[@]:+foo}" - operators are equivalent on arrays?
165
166 case $SH in dash) exit ;; esac
167
168 echo '+ ' /"${array[@]+foo}"/
169 echo '+:' /"${array[@]:+foo}"/
170 echo
171
172 typeset -a array
173 array=()
174
175 echo '+ ' /"${array[@]+foo}"/
176 echo '+:' /"${array[@]:+foo}"/
177 echo
178
179 array=('')
180
181 echo '+ ' /"${array[@]+foo}"/
182 echo '+:' /"${array[@]:+foo}"/
183 echo
184
185 array=(spam eggs)
186
187 echo '+ ' /"${array[@]+foo}"/
188 echo '+:' /"${array[@]:+foo}"/
189 echo
190
191
192 ## BUG mksh STDOUT:
193 + //
194 +: //
195
196 + //
197 +: //
198
199 + /foo/
200 +: //
201
202 + /foo/
203 +: /foo/
204
205 ## END
206
207 # Bash 2.0..4.4 has a bug that "${a[@]:-xxx}" produces an empty string. It
208 # seemed to consider a[@] and a[*] are non-empty when there is at least one
209 # element even if the element is empty. This was fixed in Bash 5.0.
210 #
211 # ## BUG bash STDOUT:
212 # + //
213 # +: //
214 #
215 # + //
216 # +: //
217 #
218 # + /foo/
219 # +: /foo/
220 #
221 # + /foo/
222 # +: /foo/
223 #
224 # ## END
225
226 ## BUG zsh STDOUT:
227 + //
228 +: //
229
230 + /foo/
231 +: //
232
233 + /foo/
234 +: /foo/
235
236 + /foo/
237 +: /foo/
238
239 ## END
240
241 ## N-I dash STDOUT:
242 ## END
243
244
245
246 #### Nix idiom ${!hooksSlice+"${!hooksSlice}"} - was workaround for obsolete bash 4.3 bug
247
248 case $SH in dash|mksh|zsh) exit ;; esac
249
250 # https://oilshell.zulipchat.com/#narrow/stream/307442-nix/topic/Replacing.20bash.20with.20osh.20in.20Nixpkgs.20stdenv
251
252 (argv.py ${!hooksSlice+"${!hooksSlice}"})
253
254 hooksSlice=x
255
256 argv.py ${!hooksSlice+"${!hooksSlice}"}
257
258 declare -a hookSlice=()
259
260 argv.py ${!hooksSlice+"${!hooksSlice}"}
261
262 foo=42
263 bar=43
264
265 declare -a hooksSlice=(foo bar spam eggs)
266
267 argv.py ${!hooksSlice+"${!hooksSlice}"}
268
269 ## STDOUT:
270 []
271 []
272 ['42']
273 ## END
274
275 # Bash 4.4 has a bug that ${!undef-} successfully generates an empty word.
276 #
277 # ## BUG bash STDOUT:
278 # []
279 # []
280 # []
281 # ['42']
282 # ## END
283
284 ## OK dash/mksh/zsh STDOUT:
285 ## END
286
287 #### ${v-foo} and ${v:-foo} when set -u
288 set -u
289 v=v
290 echo v=${v:-foo}
291 echo v=${v-foo}
292 unset v
293 echo v=${v:-foo}
294 echo v=${v-foo}
295 ## STDOUT:
296 v=v
297 v=v
298 v=foo
299 v=foo
300 ## END
301
302 #### array and - and +
303 case $SH in (dash) exit ;; esac
304
305 shopt -s compat_array # to refer to array as scalar
306
307 empty=()
308 a1=('')
309 a2=('' x)
310 a3=(3 4)
311 echo empty=${empty[@]-minus}
312 echo a1=${a1[@]-minus}
313 echo a1[0]=${a1[0]-minus}
314 echo a2=${a2[@]-minus}
315 echo a3=${a3[@]-minus}
316 echo ---
317
318 echo empty=${empty[@]+plus}
319 echo a1=${a1[@]+plus}
320 echo a1[0]=${a1[0]+plus}
321 echo a2=${a2[@]+plus}
322 echo a3=${a3[@]+plus}
323 echo ---
324
325 echo empty=${empty+plus}
326 echo a1=${a1+plus}
327 echo a2=${a2+plus}
328 echo a3=${a3+plus}
329 echo ---
330
331 # Test quoted arrays too
332 argv.py "${empty[@]-minus}"
333 argv.py "${empty[@]+plus}"
334 argv.py "${a1[@]-minus}"
335 argv.py "${a1[@]+plus}"
336 argv.py "${a1[0]-minus}"
337 argv.py "${a1[0]+plus}"
338 argv.py "${a2[@]-minus}"
339 argv.py "${a2[@]+plus}"
340 argv.py "${a3[@]-minus}"
341 argv.py "${a3[@]+plus}"
342
343 ## STDOUT:
344 empty=minus
345 a1=
346 a1[0]=
347 a2= x
348 a3=3 4
349 ---
350 empty=
351 a1=plus
352 a1[0]=plus
353 a2=plus
354 a3=plus
355 ---
356 empty=
357 a1=plus
358 a2=plus
359 a3=plus
360 ---
361 ['minus']
362 []
363 ['']
364 ['plus']
365 ['']
366 ['plus']
367 ['', 'x']
368 ['plus']
369 ['3', '4']
370 ['plus']
371 ## END
372 ## N-I dash stdout-json: ""
373 ## N-I zsh stdout-json: "empty=\na1=\n"
374 ## N-I zsh status: 1
375
376 #### $@ and - and +
377 echo argv=${@-minus}
378 echo argv=${@+plus}
379 echo argv=${@:-minus}
380 echo argv=${@:+plus}
381 ## STDOUT:
382 argv=minus
383 argv=
384 argv=minus
385 argv=
386 ## END
387 ## BUG dash/zsh STDOUT:
388 argv=
389 argv=plus
390 argv=minus
391 argv=
392 ## END
393
394 #### assoc array and - and +
395 case $SH in (dash|mksh) exit ;; esac
396
397 declare -A empty=()
398 declare -A assoc=(['k']=v)
399
400 echo empty=${empty[@]-minus}
401 echo empty=${empty[@]+plus}
402 echo assoc=${assoc[@]-minus}
403 echo assoc=${assoc[@]+plus}
404
405 echo ---
406 echo empty=${empty[@]:-minus}
407 echo empty=${empty[@]:+plus}
408 echo assoc=${assoc[@]:-minus}
409 echo assoc=${assoc[@]:+plus}
410 ## STDOUT:
411 empty=minus
412 empty=
413 assoc=v
414 assoc=plus
415 ---
416 empty=minus
417 empty=
418 assoc=v
419 assoc=plus
420 ## END
421
422 ## BUG zsh STDOUT:
423 empty=
424 empty=plus
425 assoc=minus
426 assoc=
427 ---
428 empty=minus
429 empty=
430 assoc=minus
431 assoc=
432 ## END
433
434 ## N-I dash/mksh STDOUT:
435 ## END
436
437
438 #### Error when empty
439 empty=''
440 echo ${empty:?'is em'pty} # test eval of error
441 echo should not get here
442 ## stdout-json: ""
443 ## status: 1
444 ## OK dash status: 2
445
446 #### Error when unset
447 echo ${unset?is empty}
448 echo should not get here
449 ## stdout-json: ""
450 ## status: 1
451 ## OK dash status: 2
452
453 #### Error when unset
454 v=foo
455 echo ${v+v is not unset} ${unset:+is not unset}
456 ## stdout: v is not unset
457
458 #### ${var=x} dynamic scope
459 f() { : "${hello:=x}"; echo $hello; }
460 f
461 echo hello=$hello
462
463 f() { hello=x; }
464 f
465 echo hello=$hello
466 ## STDOUT:
467 x
468 hello=x
469 hello=x
470 ## END
471
472 #### array ${arr[0]=x}
473 arr=()
474 echo ${#arr[@]}
475 : ${arr[0]=x}
476 echo ${#arr[@]}
477 ## STDOUT:
478 0
479 1
480 ## END
481 ## N-I dash status: 2
482 ## N-I dash stdout-json: ""
483 ## N-I zsh status: 1
484 ## N-I zsh stdout-json: "0\n"
485
486 #### assoc array ${arr["k"]=x}
487 # note: this also works in zsh
488
489 declare -A arr=()
490 echo ${#arr[@]}
491 : ${arr['k']=x}
492 echo ${#arr[@]}
493 ## STDOUT:
494 0
495 1
496 ## END
497 ## N-I dash status: 2
498 ## N-I dash stdout-json: ""
499 ## N-I mksh status: 1
500 ## N-I mksh stdout-json: ""
501
502 #### "\z" as arg
503 echo "${undef-\$}"
504 echo "${undef-\(}"
505 echo "${undef-\z}"
506 echo "${undef-\"}"
507 echo "${undef-\`}"
508 echo "${undef-\\}"
509 ## STDOUT:
510 $
511 \(
512 \z
513 "
514 `
515 \
516 ## END
517 ## BUG yash STDOUT:
518 $
519 (
520 z
521 "
522 `
523 \
524 ## END
525 # Note: this line terminates the quoting by ` not to confuse the text editor.
526
527
528 #### "\e" as arg
529 echo "${undef-\e}"
530 ## STDOUT:
531 \e
532 ## END
533 ## BUG zsh/mksh stdout-repr: '\x1b\n'
534 ## BUG yash stdout: e