1 ## compare_shells: dash bash-4.4 mksh zsh
2 ## oils_failures_allowed: 0
3
4 #### Eval
5 eval "a=3"
6 echo $a
7 ## stdout: 3
8
9 #### eval accepts/ignores --
10 eval -- echo hi
11 ## STDOUT:
12 hi
13 ## END
14 ## BUG dash status: 127
15 ## BUG dash stdout-json: ""
16
17 #### eval usage
18 eval -
19 echo $?
20 eval -z
21 echo $?
22 ## STDOUT:
23 127
24 2
25 ## END
26 ## OK dash STDOUT:
27 127
28 127
29 ## END
30 ## OK mksh status: 1
31 ## OK mksh STDOUT:
32 127
33 ## END
34 ## OK zsh STDOUT:
35 0
36 127
37 ## END
38
39 #### eval string with 'break continue return error'
40
41 set -e
42
43 sh_func_that_evals() {
44 local code_str=$1
45 for i in 1 2; do
46 echo $i
47 eval "$code_str"
48 done
49 echo 'end func'
50 }
51
52 for code_str in break continue return false; do
53 echo "--- $code_str"
54 sh_func_that_evals "$code_str"
55 done
56 echo status=$?
57
58 ## status: 1
59 ## STDOUT:
60 --- break
61 1
62 end func
63 --- continue
64 1
65 2
66 end func
67 --- return
68 1
69 --- false
70 1
71 ## END
72
73 ## BUG mksh STDOUT:
74 --- break
75 1
76 2
77 end func
78 --- continue
79 1
80 2
81 end func
82 --- return
83 1
84 --- false
85 1
86 ## END
87
88 #### eval YSH block with 'break continue return error'
89 case $SH in dash|bash*|mksh|zsh) exit ;; esac
90
91 shopt -s ysh:all
92
93 proc proc_that_evals(; ; ;b) {
94 for i in 1 2; do
95 echo $i
96 call io->eval(b)
97 done
98 echo 'end func'
99 }
100
101 var cases = [
102 ['break', ^(break)],
103 ['continue', ^(continue)],
104 ['return', ^(return)],
105 ['false', ^(false)],
106 ]
107
108 for test_case in (cases) {
109 var code_str, block = test_case
110 echo "--- $code_str"
111 proc_that_evals (; ; block)
112 }
113 echo status=$?
114
115 ## status: 1
116 ## STDOUT:
117 --- break
118 1
119 end func
120 --- continue
121 1
122 2
123 end func
124 --- return
125 1
126 --- false
127 1
128 ## END
129
130 ## N-I dash/bash/mksh/zsh status: 0
131 ## N-I dash/bash/mksh/zsh STDOUT:
132 ## END
133
134 #### exit within eval (regression)
135 eval 'exit 42'
136 echo 'should not get here'
137 ## stdout-json: ""
138 ## status: 42
139
140 #### exit within source (regression)
141 cd $TMP
142 echo 'exit 42' > lib.sh
143 . ./lib.sh
144 echo 'should not get here'
145 ## stdout-json: ""
146 ## status: 42
147
148 #### Source
149 lib=$TMP/spec-test-lib.sh
150 echo 'LIBVAR=libvar' > $lib
151 . $lib # dash doesn't have source
152 echo $LIBVAR
153 ## stdout: libvar
154
155 #### source accepts/ignores --
156 echo 'echo foo' > $TMP/foo.sh
157 source -- $TMP/foo.sh
158 ## STDOUT:
159 foo
160 ## END
161 ## N-I dash stdout-json: ""
162 ## N-I dash status: 127
163
164 #### Source nonexistent
165 source /nonexistent/path
166 echo status=$?
167 ## stdout: status=1
168 ## OK dash/zsh stdout: status=127
169
170 #### Source with no arguments
171 source
172 echo status=$?
173 ## stdout: status=2
174 ## OK mksh/zsh stdout: status=1
175 ## N-I dash stdout: status=127
176
177 #### Source with arguments
178 . $REPO_ROOT/spec/testdata/show-argv.sh foo bar # dash doesn't have source
179 ## STDOUT:
180 show-argv: foo bar
181 ## END
182 ## N-I dash STDOUT:
183 show-argv:
184 ## END
185
186 #### Source from a function, mutating argv and defining a local var
187 f() {
188 . $REPO_ROOT/spec/testdata/source-argv.sh # no argv
189 . $REPO_ROOT/spec/testdata/source-argv.sh args to src # new argv
190 echo $@
191 echo foo=$foo # defined in source-argv.sh
192 }
193 f args to func
194 echo foo=$foo # not defined
195 ## STDOUT:
196 source-argv: args to func
197 source-argv: args to src
198 to func
199 foo=foo_val
200 foo=
201 ## END
202 ## N-I dash STDOUT:
203 source-argv: args to func
204 source-argv: to func
205 func
206 foo=foo_val
207 foo=
208 ## END
209
210 #### Source with syntax error
211 # TODO: We should probably use dash behavior of a fatal error.
212 # Although set-o errexit handles this. We don't want to break the invariant
213 # that a builtin like 'source' behaves like an external program. An external
214 # program can't halt the shell!
215 echo 'echo >' > $TMP/syntax-error.sh
216 . $TMP/syntax-error.sh
217 echo status=$?
218 ## stdout: status=2
219 ## OK bash/mksh stdout: status=1
220 ## OK zsh stdout: status=126
221 ## OK dash stdout-json: ""
222 ## OK dash status: 2
223
224 #### Eval with syntax error
225 eval 'echo >'
226 echo status=$?
227 ## stdout: status=2
228 ## OK bash/zsh stdout: status=1
229 ## OK dash stdout-json: ""
230 ## OK dash status: 2
231 ## OK mksh stdout-json: ""
232 ## OK mksh status: 1
233
234 #### Eval in does tilde expansion
235
236 x="~"
237 eval y="$x" # scalar
238 test "$x" = "$y" || echo FALSE
239 [[ $x == /* ]] || echo FALSE # doesn't start with /
240 [[ $y == /* ]] && echo TRUE
241
242 #argv "$x" "$y"
243
244 ## STDOUT:
245 FALSE
246 FALSE
247 TRUE
248 ## END
249 ## BUG dash status: 127
250 ## BUG dash STDOUT:
251 FALSE
252 ## END
253 ## BUG mksh status: 1
254 ## BUG mksh STDOUT:
255 FALSE
256 ## END
257
258 #### Eval in bash does tilde expansion in array
259
260 # the "make" plugin in bash-completion relies on this? wtf?
261 x="~"
262
263 # UPSTREAM CODE
264
265 #eval array=( "$x" )
266
267 # FIXED CODE -- proper quoting.
268
269 eval 'array=(' "$x" ')' # array
270
271 test "$x" = "${array[0]}" || echo FALSE
272 [[ $x == /* ]] || echo FALSE # doesn't start with /
273 [[ "${array[0]}" == /* ]] && echo TRUE
274 ## STDOUT:
275 FALSE
276 FALSE
277 TRUE
278 ## END
279 ## N-I dash status: 2
280 ## N-I dash stdout-json: ""
281 ## BUG mksh status: 1
282 ## BUG mksh STDOUT:
283 FALSE
284 ## END
285 ## BUG zsh status: 1
286 ## BUG zsh STDOUT:
287 FALSE
288 FALSE
289 ## END
290
291 #### source works for files in current directory (bash only)
292 cd $TMP
293 echo "echo current dir" > cmd
294 . cmd
295 echo status=$?
296 ## STDOUT:
297 current dir
298 status=0
299 ## END
300 ## N-I zsh STDOUT:
301 status=127
302 ## END
303
304 # This is a special builtin so failure is fatal.
305
306 ## N-I dash stdout-json: ""
307 ## N-I dash status: 2
308 ## N-I mksh stdout-json: ""
309 ## N-I mksh status: 1
310
311 #### source looks in PATH for files
312 mkdir -p dir
313 echo "echo hi" > dir/cmd
314 PATH="dir:$PATH"
315 . cmd
316 rm dir/cmd
317 ## STDOUT:
318 hi
319 ## END
320
321 #### source finds files in PATH before current dir
322 cd $TMP
323 mkdir -p dir
324 echo "echo path" > dir/cmd
325 echo "echo current dir" > cmd
326 PATH="dir:$PATH"
327 . cmd
328 echo status=$?
329 ## STDOUT:
330 path
331 status=0
332 ## END
333
334 #### source works for files in subdirectory
335 mkdir -p dir
336 echo "echo path" > dir/cmd
337 . dir/cmd
338 rm dir/cmd
339 ## STDOUT:
340 path
341 ## END
342
343 #### source doesn't crash when targeting a directory
344 cd $TMP
345 mkdir -p dir
346 . ./dir/
347 echo status=$?
348 ## stdout: status=1
349 ## OK dash/zsh/mksh stdout: status=0