1 ## compare_shells: bash-4.4 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 ## BUG bash STDOUT:
51 ['Xx1', '2', '3', '4xX']
52 ['Xx1 2 3 4xX']
53 ## END
54 ## OK osh STDOUT:
55 ['Xx1 2', '3 4xX']
56 ['Xx1 2 3 4xX']
57 ## END
58
59 #### Assign default with array
60 set -- '1 2' '3 4'
61 argv.py X${unset=x"$@"x}X
62 argv.py "$unset"
63 ## STDOUT:
64 ['Xx1', '2', '3', '4xX']
65 ['x1 2 3 4x']
66 ## END
67 ## OK osh STDOUT:
68 ['Xx1 2', '3 4xX']
69 ['x1 2 3 4x']
70 ## END
71 ## OK zsh STDOUT:
72 ['Xx1 2 3 4xX']
73 ['x1 2 3 4x']
74 ## END
75
76 #### Assign default value when empty
77 empty=''
78 ${empty:=is empty}
79 echo $empty
80 ## stdout: is empty
81
82 #### Assign default value when unset
83 ${unset=is unset}
84 echo $unset
85 ## stdout: is unset
86
87 #### ${v:+foo} Alternative value when empty
88 v=foo
89 empty=''
90 echo ${v:+v is not empty} ${empty:+is not empty}
91 ## stdout: v is not empty
92
93 #### ${v+foo} Alternative value when unset
94 v=foo
95 echo ${v+v is not unset} ${unset:+is not unset}
96 ## stdout: v is not unset
97
98 #### "${x+foo}" quoted (regression)
99 # Python's configure caught this
100 argv.py "${with_icc+set}" = set
101 ## STDOUT:
102 ['', '=', 'set']
103 ## END
104
105 #### ${s+foo} and ${s:+foo} when set -u
106 set -u
107 v=v
108 echo v=${v:+foo}
109 echo v=${v+foo}
110 unset v
111 echo v=${v:+foo}
112 echo v=${v+foo}
113 ## STDOUT:
114 v=foo
115 v=foo
116 v=
117 v=
118 ## END
119
120 #### "${array[@]} with set -u (bash is outlier)
121 case $SH in dash) exit ;; esac
122
123 set -u
124
125 typeset -a empty
126 empty=()
127
128 echo empty /"${empty[@]}"/
129 echo undefined /"${undefined[@]}"/
130
131 ## status: 1
132 ## STDOUT:
133 empty //
134 ## END
135
136 ## BUG bash status: 0
137 ## BUG bash STDOUT:
138 empty //
139 undefined //
140 ## END
141
142 # empty array is unset in mksh
143 ## BUG mksh status: 1
144 ## BUG mksh STDOUT:
145 ## END
146
147 ## N-I dash status: 0
148 ## N-I dash STDOUT:
149 ## END
150
151
152 #### "${undefined[@]+foo}" and "${undefined[@]:+foo}", with set -u
153 case $SH in dash) exit ;; esac
154
155 set -u
156
157 echo plus /"${array[@]+foo}"/
158 echo plus colon /"${array[@]:+foo}"/
159
160 ## STDOUT:
161 plus //
162 plus colon //
163 ## END
164
165 ## N-I dash STDOUT:
166 ## END
167
168 #### "${a[@]+foo}" and "${a[@]:+foo}" - operators are equivalent on arrays?
169
170 case $SH in dash) exit ;; esac
171
172 echo '+ ' /"${array[@]+foo}"/
173 echo '+:' /"${array[@]:+foo}"/
174 echo
175
176 typeset -a array
177 array=()
178
179 echo '+ ' /"${array[@]+foo}"/
180 echo '+:' /"${array[@]:+foo}"/
181 echo
182
183 array=('')
184
185 echo '+ ' /"${array[@]+foo}"/
186 echo '+:' /"${array[@]:+foo}"/
187 echo
188
189 array=(spam eggs)
190
191 echo '+ ' /"${array[@]+foo}"/
192 echo '+:' /"${array[@]:+foo}"/
193 echo
194
195
196 ## STDOUT:
197 + //
198 +: //
199
200 + //
201 +: //
202
203 + /foo/
204 +: /foo/
205
206 + /foo/
207 +: /foo/
208
209 ## END
210
211 ## BUG mksh STDOUT:
212 + //
213 +: //
214
215 + //
216 +: //
217
218 + /foo/
219 +: //
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-} is successfully generate 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
526 #### "\e" as arg
527 echo "${undef-\e}"
528 ## STDOUT:
529 \e
530 ## END
531 ## BUG zsh/mksh stdout-repr: '\x1b\n'
532 ## BUG yash stdout: e
533