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 |
|