1 | ## compare_shells: dash bash-4.4 mksh
|
2 |
|
3 | # In this file:
|
4 | #
|
5 | # - strict_control-flow: break/continue at the top level should be fatal!
|
6 | #
|
7 | # Other tests:
|
8 | # - spec/errexit-strict: command subs inherit errexit
|
9 | # - TODO: does bash 4.4. use inherit_errexit?
|
10 | #
|
11 | # - spec/var-op-other tests strict_word-eval (negative indices and invalid
|
12 | # utf-8)
|
13 | # - hm I think these should be the default? compat-word-eval?
|
14 | #
|
15 | # - spec/arith tests strict_arith - invalid strings become 0
|
16 | # - OSH has a warning that can turn into an error. I think the error could
|
17 | # be the default (since this was a side effect of "ShellMathShock")
|
18 |
|
19 | # - strict_array: unimplemented.
|
20 | # - WAS undef[2]=x, but bash-completion relied on the associative array
|
21 | # version of that.
|
22 | # - TODO: It should disable decay_array EVERYWHERE except a specific case like:
|
23 | # - s="${a[*]}" # quoted, the unquoted ones glob in a command context
|
24 | # - spec/dbracket has array comparison relevant to the case below
|
25 | #
|
26 | # Most of those options could be compat-*.
|
27 | #
|
28 | # One that can't: strict_scope disables dynamic scope.
|
29 |
|
30 |
|
31 | #### strict_arith option
|
32 | shopt -s strict_arith
|
33 | ## status: 0
|
34 | ## N-I bash status: 1
|
35 | ## N-I dash/mksh status: 127
|
36 |
|
37 | #### Sourcing a script that returns at the top level
|
38 | echo one
|
39 | . $REPO_ROOT/spec/testdata/return-helper.sh
|
40 | echo $?
|
41 | echo two
|
42 | ## STDOUT:
|
43 | one
|
44 | return-helper.sh
|
45 | 42
|
46 | two
|
47 | ## END
|
48 |
|
49 | #### top level control flow
|
50 | $SH $REPO_ROOT/spec/testdata/top-level-control-flow.sh
|
51 | ## status: 0
|
52 | ## STDOUT:
|
53 | SUBSHELL
|
54 | BREAK
|
55 | CONTINUE
|
56 | RETURN
|
57 | ## OK bash STDOUT:
|
58 | SUBSHELL
|
59 | BREAK
|
60 | CONTINUE
|
61 | RETURN
|
62 | DONE
|
63 | ## END
|
64 |
|
65 | #### errexit and top-level control flow
|
66 | $SH -o errexit $REPO_ROOT/spec/testdata/top-level-control-flow.sh
|
67 | ## status: 2
|
68 | ## OK bash status: 1
|
69 | ## STDOUT:
|
70 | SUBSHELL
|
71 | ## END
|
72 |
|
73 | #### shopt -s strict_control_flow
|
74 | shopt -s strict_control_flow || true
|
75 | echo break
|
76 | break
|
77 | echo hi
|
78 | ## STDOUT:
|
79 | break
|
80 | ## END
|
81 | ## status: 1
|
82 | ## N-I dash/bash/mksh STDOUT:
|
83 | break
|
84 | hi
|
85 | # END
|
86 | ## N-I dash/bash/mksh status: 0
|
87 |
|
88 | #### return at top level is an error
|
89 | return
|
90 | echo "status=$?"
|
91 | ## stdout-json: ""
|
92 | ## OK bash STDOUT:
|
93 | status=1
|
94 | ## END
|
95 |
|
96 | #### continue at top level is NOT an error
|
97 | # NOTE: bash and mksh both print warnings, but don't exit with an error.
|
98 | continue
|
99 | echo status=$?
|
100 | ## stdout: status=0
|
101 |
|
102 | #### break at top level is NOT an error
|
103 | break
|
104 | echo status=$?
|
105 | ## stdout: status=0
|
106 |
|
107 | #### empty argv WITHOUT strict_argv
|
108 | x=''
|
109 | $x
|
110 | echo status=$?
|
111 |
|
112 | if $x; then
|
113 | echo VarSub
|
114 | fi
|
115 |
|
116 | if $(echo foo >/dev/null); then
|
117 | echo CommandSub
|
118 | fi
|
119 |
|
120 | if "$x"; then
|
121 | echo VarSub
|
122 | else
|
123 | echo VarSub FAILED
|
124 | fi
|
125 |
|
126 | if "$(echo foo >/dev/null)"; then
|
127 | echo CommandSub
|
128 | else
|
129 | echo CommandSub FAILED
|
130 | fi
|
131 |
|
132 | ## STDOUT:
|
133 | status=0
|
134 | VarSub
|
135 | CommandSub
|
136 | VarSub FAILED
|
137 | CommandSub FAILED
|
138 | ## END
|
139 |
|
140 | #### empty argv WITH strict_argv
|
141 | shopt -s strict_argv || true
|
142 | echo empty
|
143 | x=''
|
144 | $x
|
145 | echo status=$?
|
146 | ## status: 1
|
147 | ## STDOUT:
|
148 | empty
|
149 | ## END
|
150 | ## N-I dash/bash/mksh status: 0
|
151 | ## N-I dash/bash/mksh STDOUT:
|
152 | empty
|
153 | status=0
|
154 | ## END
|
155 |
|
156 | #### Arrays are incorrectly compared, but strict_array prevents it
|
157 |
|
158 | # NOTE: from spec/dbracket has a test case like this
|
159 | # sane-array should turn this ON.
|
160 | # bash and mksh allow this because of decay
|
161 |
|
162 | a=('a b' 'c d')
|
163 | b=('a' 'b' 'c' 'd')
|
164 | echo ${#a[@]}
|
165 | echo ${#b[@]}
|
166 | [[ "${a[@]}" == "${b[@]}" ]] && echo EQUAL
|
167 |
|
168 | shopt -s strict_array || true
|
169 | [[ "${a[@]}" == "${b[@]}" ]] && echo EQUAL
|
170 |
|
171 | ## status: 1
|
172 | ## STDOUT:
|
173 | 2
|
174 | 4
|
175 | EQUAL
|
176 | ## END
|
177 | ## OK bash/mksh status: 0
|
178 | ## OK bash/mksh STDOUT:
|
179 | 2
|
180 | 4
|
181 | EQUAL
|
182 | EQUAL
|
183 | ## END
|
184 | ## N-I dash status: 2
|
185 | ## N-I dash stdout-json: ""
|
186 |
|
187 | #### automatically creating arrays WITHOUT strict_array
|
188 | undef[2]=x
|
189 | undef[3]=y
|
190 | argv.py "${undef[@]}"
|
191 | ## STDOUT:
|
192 | ['x', 'y']
|
193 | ## END
|
194 | ## N-I dash status: 2
|
195 | ## N-I dash stdout-json: ""
|
196 |
|
197 | #### automatically creating arrays are INDEXED, not associative
|
198 | shopt -u strict_arith || true
|
199 |
|
200 | undef[2]=x
|
201 | undef[3]=y
|
202 | x='bad'
|
203 | # bad gets coerced to zero, but this is part of the RECURSIVE arithmetic
|
204 | # behavior, which we want to disallow. Consider disallowing in OSH.
|
205 |
|
206 | undef[$x]=zzz
|
207 | argv.py "${undef[@]}"
|
208 | ## STDOUT:
|
209 | ['zzz', 'x', 'y']
|
210 | ## END
|
211 | ## N-I dash status: 2
|
212 | ## N-I dash stdout-json: ""
|
213 |
|
214 | #### simple_eval_builtin
|
215 | for i in 1 2; do
|
216 | eval # zero args
|
217 | echo status=$?
|
218 | eval echo one
|
219 | echo status=$?
|
220 | eval 'echo two'
|
221 | echo status=$?
|
222 | shopt -s simple_eval_builtin
|
223 | echo ---
|
224 | done
|
225 | ## STDOUT:
|
226 | status=0
|
227 | one
|
228 | status=0
|
229 | two
|
230 | status=0
|
231 | ---
|
232 | status=2
|
233 | status=2
|
234 | two
|
235 | status=0
|
236 | ---
|
237 | ## END
|
238 | ## N-I dash/bash/mksh STDOUT:
|
239 | status=0
|
240 | one
|
241 | status=0
|
242 | two
|
243 | status=0
|
244 | ---
|
245 | status=0
|
246 | one
|
247 | status=0
|
248 | two
|
249 | status=0
|
250 | ---
|
251 | ## END
|
252 |
|
253 |
|
254 | #### strict_parse_slice means you need explicit length
|
255 | case $SH in bash*|dash|mksh) exit ;; esac
|
256 |
|
257 | $SH -c '
|
258 | a=(1 2 3); echo /${a[@]::}/
|
259 | '
|
260 | echo status=$?
|
261 |
|
262 | $SH -c '
|
263 | shopt --set strict_parse_slice
|
264 |
|
265 | a=(1 2 3); echo /${a[@]::}/
|
266 | '
|
267 | echo status=$?
|
268 |
|
269 | ## STDOUT:
|
270 | //
|
271 | status=0
|
272 | status=2
|
273 | ## END
|
274 |
|
275 | ## N-I bash/dash/mksh STDOUT:
|
276 | ## END
|
277 |
|
278 |
|
279 | #### Control flow must be static in YSH (strict_control_flow)
|
280 | case $SH in bash*|dash|mksh) exit ;; esac
|
281 |
|
282 | shopt --set ysh:all
|
283 |
|
284 | for x in a b c {
|
285 | echo $x
|
286 | if (x === 'a') {
|
287 | break
|
288 | }
|
289 | }
|
290 |
|
291 | echo ---
|
292 |
|
293 | for keyword in break continue return exit {
|
294 | try {
|
295 | $[ENV.SH] -o ysh:all -c '
|
296 | var k = $1
|
297 | for x in a b c {
|
298 | echo $x
|
299 | if (x === "a") {
|
300 | $k
|
301 | }
|
302 | }
|
303 | ' unused $keyword
|
304 | }
|
305 | echo code=$[_error.code]
|
306 | echo '==='
|
307 | }
|
308 |
|
309 | ## STDOUT:
|
310 | a
|
311 | ---
|
312 | a
|
313 | code=1
|
314 | ===
|
315 | a
|
316 | code=1
|
317 | ===
|
318 | a
|
319 | code=1
|
320 | ===
|
321 | a
|
322 | code=1
|
323 | ===
|
324 | ## END
|
325 |
|
326 | ## N-I bash/dash/mksh STDOUT:
|
327 | ## END
|
328 |
|
329 | #### shopt -s strict_binding: Persistent prefix bindings not allowed on special builtins
|
330 |
|
331 | shopt --set strict:all
|
332 |
|
333 | # This differs from what it means in a process
|
334 | FOO=bar eval 'echo FOO=$FOO'
|
335 | echo FOO=$FOO
|
336 |
|
337 | ## status: 1
|
338 | ## STDOUT:
|
339 | ## END
|
340 |
|
341 | ## BUG bash status: 0
|
342 | ## BUG bash STDOUT:
|
343 | FOO=bar
|
344 | FOO=
|
345 | ## END
|
346 |
|
347 | ## N-I dash/mksh status: 0
|
348 | ## N-I dash/mksh STDOUT:
|
349 | FOO=bar
|
350 | FOO=bar
|
351 | ## END
|