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 declare -a hookSlice=()
255
256 argv.py ${!hooksSlice+"${!hooksSlice}"}
257
258 foo=42
259 bar=43
260
261 declare -a hookSlice=(foo bar spam eggs)
262
263 argv.py ${!hooksSlice+"${!hooksSlice}"}
264
265 ## STDOUT:
266 []
267 []
268 []
269 ## END
270
271 ## OK dash/mksh/zsh STDOUT:
272 ## END
273
274 #### ${v-foo} and ${v:-foo} when set -u
275 set -u
276 v=v
277 echo v=${v:-foo}
278 echo v=${v-foo}
279 unset v
280 echo v=${v:-foo}
281 echo v=${v-foo}
282 ## STDOUT:
283 v=v
284 v=v
285 v=foo
286 v=foo
287 ## END
288
289 #### array and - and +
290 case $SH in (dash) exit ;; esac
291
292 shopt -s compat_array # to refer to array as scalar
293
294 empty=()
295 a1=('')
296 a2=('' x)
297 a3=(3 4)
298 echo empty=${empty[@]-minus}
299 echo a1=${a1[@]-minus}
300 echo a1[0]=${a1[0]-minus}
301 echo a2=${a2[@]-minus}
302 echo a3=${a3[@]-minus}
303 echo ---
304
305 echo empty=${empty[@]+plus}
306 echo a1=${a1[@]+plus}
307 echo a1[0]=${a1[0]+plus}
308 echo a2=${a2[@]+plus}
309 echo a3=${a3[@]+plus}
310 echo ---
311
312 echo empty=${empty+plus}
313 echo a1=${a1+plus}
314 echo a2=${a2+plus}
315 echo a3=${a3+plus}
316 echo ---
317
318 # Test quoted arrays too
319 argv.py "${empty[@]-minus}"
320 argv.py "${empty[@]+plus}"
321 argv.py "${a1[@]-minus}"
322 argv.py "${a1[@]+plus}"
323 argv.py "${a1[0]-minus}"
324 argv.py "${a1[0]+plus}"
325 argv.py "${a2[@]-minus}"
326 argv.py "${a2[@]+plus}"
327 argv.py "${a3[@]-minus}"
328 argv.py "${a3[@]+plus}"
329
330 ## STDOUT:
331 empty=minus
332 a1=
333 a1[0]=
334 a2= x
335 a3=3 4
336 ---
337 empty=
338 a1=plus
339 a1[0]=plus
340 a2=plus
341 a3=plus
342 ---
343 empty=
344 a1=plus
345 a2=plus
346 a3=plus
347 ---
348 ['minus']
349 []
350 ['']
351 ['plus']
352 ['']
353 ['plus']
354 ['', 'x']
355 ['plus']
356 ['3', '4']
357 ['plus']
358 ## END
359 ## N-I dash stdout-json: ""
360 ## N-I zsh stdout-json: "empty=\na1=\n"
361 ## N-I zsh status: 1
362
363 #### $@ and - and +
364 echo argv=${@-minus}
365 echo argv=${@+plus}
366 echo argv=${@:-minus}
367 echo argv=${@:+plus}
368 ## STDOUT:
369 argv=minus
370 argv=
371 argv=minus
372 argv=
373 ## END
374 ## BUG dash/zsh STDOUT:
375 argv=
376 argv=plus
377 argv=minus
378 argv=
379 ## END
380
381 #### assoc array and - and +
382 case $SH in (dash|mksh) exit ;; esac
383
384 declare -A empty=()
385 declare -A assoc=(['k']=v)
386
387 echo empty=${empty[@]-minus}
388 echo empty=${empty[@]+plus}
389 echo assoc=${assoc[@]-minus}
390 echo assoc=${assoc[@]+plus}
391
392 echo ---
393 echo empty=${empty[@]:-minus}
394 echo empty=${empty[@]:+plus}
395 echo assoc=${assoc[@]:-minus}
396 echo assoc=${assoc[@]:+plus}
397 ## STDOUT:
398 empty=minus
399 empty=
400 assoc=v
401 assoc=plus
402 ---
403 empty=minus
404 empty=
405 assoc=v
406 assoc=plus
407 ## END
408
409 ## BUG zsh STDOUT:
410 empty=
411 empty=plus
412 assoc=minus
413 assoc=
414 ---
415 empty=minus
416 empty=
417 assoc=minus
418 assoc=
419 ## END
420
421 ## N-I dash/mksh STDOUT:
422 ## END
423
424
425 #### Error when empty
426 empty=''
427 echo ${empty:?'is em'pty} # test eval of error
428 echo should not get here
429 ## stdout-json: ""
430 ## status: 1
431 ## OK dash status: 2
432
433 #### Error when unset
434 echo ${unset?is empty}
435 echo should not get here
436 ## stdout-json: ""
437 ## status: 1
438 ## OK dash status: 2
439
440 #### Error when unset
441 v=foo
442 echo ${v+v is not unset} ${unset:+is not unset}
443 ## stdout: v is not unset
444
445 #### ${var=x} dynamic scope
446 f() { : "${hello:=x}"; echo $hello; }
447 f
448 echo hello=$hello
449
450 f() { hello=x; }
451 f
452 echo hello=$hello
453 ## STDOUT:
454 x
455 hello=x
456 hello=x
457 ## END
458
459 #### array ${arr[0]=x}
460 arr=()
461 echo ${#arr[@]}
462 : ${arr[0]=x}
463 echo ${#arr[@]}
464 ## STDOUT:
465 0
466 1
467 ## END
468 ## N-I dash status: 2
469 ## N-I dash stdout-json: ""
470 ## N-I zsh status: 1
471 ## N-I zsh stdout-json: "0\n"
472
473 #### assoc array ${arr["k"]=x}
474 # note: this also works in zsh
475
476 declare -A arr=()
477 echo ${#arr[@]}
478 : ${arr['k']=x}
479 echo ${#arr[@]}
480 ## STDOUT:
481 0
482 1
483 ## END
484 ## N-I dash status: 2
485 ## N-I dash stdout-json: ""
486 ## N-I mksh status: 1
487 ## N-I mksh stdout-json: ""
488
489 #### "\z" as arg
490 echo "${undef-\$}"
491 echo "${undef-\(}"
492 echo "${undef-\z}"
493 echo "${undef-\"}"
494 echo "${undef-\`}"
495 echo "${undef-\\}"
496 ## STDOUT:
497 $
498 \(
499 \z
500 "
501 `
502 \
503 ## END
504 ## BUG yash STDOUT:
505 $
506 (
507 z
508 "
509 `
510 \
511 ## END
512
513 #### "\e" as arg
514 echo "${undef-\e}"
515 ## STDOUT:
516 \e
517 ## END
518 ## BUG zsh/mksh stdout-repr: '\x1b\n'
519 ## BUG yash stdout: e
520