1 ## oils_failures_allowed: 2
2 ## compare_shells: dash bash mksh zsh
3
4 #### exec builtin
5 exec echo hi
6 ## stdout: hi
7
8 #### exec builtin with redirects
9 exec 1>&2
10 echo 'to stderr'
11 ## stdout-json: ""
12 ## stderr: to stderr
13
14 #### exec builtin with here doc
15 # This has in a separate file because both code and data can be read from
16 # stdin.
17 $SH $REPO_ROOT/spec/bin/builtins-exec-here-doc-helper.sh
18 ## STDOUT:
19 x=one
20 y=two
21 DONE
22 ## END
23
24 #### exec builtin accepts --
25 exec -- echo hi
26 ## STDOUT:
27 hi
28 ## END
29 ## BUG dash status: 127
30 ## BUG dash stdout-json: ""
31
32 #### exec -- 2>&1
33 exec -- 3>&1
34 echo stdout 1>&3
35 ## STDOUT:
36 stdout
37 ## END
38 ## BUG dash status: 127
39 ## BUG dash stdout-json: ""
40 ## BUG mksh status: -11
41 ## BUG mksh stdout-json: ""
42
43 #### cd and $PWD
44 cd /
45 echo $PWD
46 ## stdout: /
47
48 #### cd BAD/..
49
50 # Odd divergence in shells: dash and mksh normalize the path and don't check
51 # this error.
52 # TODO: I would like OSH to behave like bash and zsh, but it separating chdir_arg and
53 # pwd_arg breaks case 17.
54
55 cd nonexistent_ZZ/..
56 echo status=$?
57 ## STDOUT:
58 status=1
59 ## END
60 ## BUG dash/mksh STDOUT:
61 status=0
62 ## END
63
64 #### $OLDPWD
65 cd /
66 cd $TMP
67 echo "old: $OLDPWD"
68 env | grep OLDPWD # It's EXPORTED too!
69 cd -
70 ## STDOUT:
71 old: /
72 OLDPWD=/
73 /
74 ## END
75 ## BUG mksh STDOUT:
76 old: /
77 /
78 ## END
79 ## BUG zsh STDOUT:
80 old: /
81 OLDPWD=/
82 ## END
83
84 #### pwd
85 cd /
86 pwd
87 ## STDOUT:
88 /
89 ## END
90
91 #### pwd after cd ..
92 dir=$TMP/dir-one/dir-two
93 mkdir -p $dir
94 cd $dir
95 echo $(basename $(pwd))
96 cd ..
97 echo $(basename $(pwd))
98 ## STDOUT:
99 dir-two
100 dir-one
101 ## END
102
103 #### pwd with symlink and -P
104 tmp=$TMP/builtins-pwd-1
105 mkdir -p $tmp/target
106 ln -s -f $tmp/target $tmp/symlink
107
108 cd $tmp/symlink
109
110 echo pwd:
111 basename $(pwd)
112
113 echo pwd -P:
114 basename $(pwd -P)
115
116 ## STDOUT:
117 pwd:
118 symlink
119 pwd -P:
120 target
121 ## END
122
123 #### setting $PWD doesn't affect the value of 'pwd' builtin
124 dir=/tmp/oil-spec-test/pwd
125 mkdir -p $dir
126 cd $dir
127
128 PWD=foo
129 echo before $PWD
130 pwd
131 echo after $PWD
132 ## STDOUT:
133 before foo
134 /tmp/oil-spec-test/pwd
135 after foo
136 ## END
137
138 #### unset PWD; then pwd
139 dir=/tmp/oil-spec-test/pwd
140 mkdir -p $dir
141 cd $dir
142
143 unset PWD
144 echo PWD=$PWD
145 pwd
146 echo PWD=$PWD
147 ## STDOUT:
148 PWD=
149 /tmp/oil-spec-test/pwd
150 PWD=
151 ## END
152
153 #### 'unset PWD; pwd' before any cd (tickles a rare corner case)
154 dir=/tmp/oil-spec-test/pwd-2
155 mkdir -p $dir
156 cd $dir
157
158 # ensure clean shell process state
159 $SH -c 'unset PWD; pwd'
160
161 ## STDOUT:
162 /tmp/oil-spec-test/pwd-2
163 ## END
164
165 #### lie about PWD; pwd before any cd
166 dir=/tmp/oil-spec-test/pwd-3
167 mkdir -p $dir
168 cd $dir
169
170 # ensure clean shell process state
171 $SH -c 'PWD=foo; pwd'
172
173 ## STDOUT:
174 /tmp/oil-spec-test/pwd-3
175 ## END
176
177 #### remove pwd dir
178 dir=/tmp/oil-spec-test/pwd
179 mkdir -p $dir
180 cd $dir
181 pwd
182 rmdir $dir
183 echo status=$?
184 pwd
185 echo status=$?
186 ## STDOUT:
187 /tmp/oil-spec-test/pwd
188 status=0
189 /tmp/oil-spec-test/pwd
190 status=0
191 ## END
192 ## OK mksh STDOUT:
193 /tmp/oil-spec-test/pwd
194 status=0
195 status=1
196 ## END
197
198 #### pwd in symlinked dir on shell initialization
199 tmp=$TMP/builtins-pwd-2
200 mkdir -p $tmp
201 mkdir -p $tmp/target
202 ln -s -f $tmp/target $tmp/symlink
203
204 cd $tmp/symlink
205 $SH -c 'basename $(pwd)'
206 unset PWD
207 $SH -c 'basename $(pwd)'
208
209 ## STDOUT:
210 symlink
211 target
212 ## END
213 ## OK mksh STDOUT:
214 target
215 target
216 ## END
217 ## stderr-json: ""
218
219 #### Test the current directory after 'cd ..' involving symlinks
220 dir=$TMP/symlinktest
221 mkdir -p $dir
222 cd $dir
223 mkdir -p a/b/c
224 mkdir -p a/b/d
225 ln -s -f a/b/c c > /dev/null
226 cd c
227 cd ..
228 # Expecting a c/ (since we are in symlinktest) but osh gives c d (thinks we are
229 # in b/)
230 ls
231 ## STDOUT:
232 a
233 c
234 ## END
235
236 #### cd with no arguments
237 HOME=$TMP/home
238 mkdir -p $HOME
239 cd
240 test $(pwd) = "$HOME" && echo OK
241 ## stdout: OK
242
243 #### cd to nonexistent dir
244 cd /nonexistent/dir
245 echo status=$?
246 ## stdout: status=1
247 ## OK dash/mksh stdout: status=2
248
249 #### cd away from dir that was deleted
250 dir=$TMP/cd-nonexistent
251 mkdir -p $dir
252 cd $dir
253 rmdir $dir
254 cd $TMP
255 echo $(basename $OLDPWD)
256 echo status=$?
257 ## STDOUT:
258 cd-nonexistent
259 status=0
260 ## END
261
262 #### cd permits double bare dash
263 cd -- /
264 echo $PWD
265 ## stdout: /
266
267 #### cd to symlink with -L and -P
268 targ=$TMP/cd-symtarget
269 lnk=$TMP/cd-symlink
270 mkdir -p $targ
271 ln -s $targ $lnk
272
273 # -L behavior is the default
274 cd $lnk
275 test $PWD = "$TMP/cd-symlink" && echo OK
276
277 cd -L $lnk
278 test $PWD = "$TMP/cd-symlink" && echo OK
279
280 cd -P $lnk
281 test $PWD = "$TMP/cd-symtarget" && echo OK || echo $PWD
282 ## STDOUT:
283 OK
284 OK
285 OK
286 ## END
287
288 #### cd to relative path with -L and -P
289 die() { echo "$@"; exit 1; }
290
291 targ=$TMP/cd-symtarget/subdir
292 lnk=$TMP/cd-symlink
293 mkdir -p $targ
294 ln -s $TMP/cd-symtarget $lnk
295
296 # -L behavior is the default
297 cd $lnk/subdir
298 test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
299 cd ..
300 test $PWD = "$TMP/cd-symlink" && echo OK
301
302 cd $lnk/subdir
303 test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
304 cd -L ..
305 test $PWD = "$TMP/cd-symlink" && echo OK
306
307 cd $lnk/subdir
308 test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
309 cd -P ..
310 test $PWD = "$TMP/cd-symtarget" && echo OK || echo $PWD
311 ## STDOUT:
312 OK
313 OK
314 OK
315 ## END
316
317 #### Exit out of function
318 f() { exit 3; }
319 f
320 exit 4
321 ## status: 3
322
323 #### Exit builtin with invalid arg
324 exit invalid
325 # Rationale: runtime errors are 1
326 ## status: 1
327 ## OK dash/bash status: 2
328 ## BUG zsh status: 0
329
330 #### Exit builtin with too many args
331 # This is a parse error in OSH.
332 exit 7 8 9
333 echo status=$?
334 ## status: 2
335 ## stdout-json: ""
336 ## BUG bash/zsh status: 0
337 ## BUG bash/zsh stdout: status=1
338 ## BUG dash status: 7
339 ## BUG dash stdout-json: ""
340 ## OK mksh status: 1
341 ## OK mksh stdout-json: ""
342
343 #### time with brace group argument
344
345 err=_tmp/time-$(basename $SH).txt
346 {
347 time {
348 sleep 0.01
349 sleep 0.02
350 }
351 } 2> $err
352
353 grep --only-matching user $err
354 echo result=$?
355
356 # Regression: check fractional seconds
357 gawk '
358 BEGIN { ok = 0 }
359 match( $0, /\.([0-9]+)/, m) {
360 if (m[1] > 0) { # check fractional seconds
361 ok = 1
362 }
363 }
364 END { if (ok) { print "non-zero" } }
365 ' $err
366
367 ## status: 0
368 ## STDOUT:
369 user
370 result=0
371 non-zero
372 ## END
373
374 # time doesn't accept a block?
375 ## BUG zsh STDOUT:
376 result=1
377 ## END
378
379 # dash doesn't have time keyword
380 ## N-I dash status: 2
381 ## N-I dash stdout-json: ""
382
383 #### time pipeline
384 time echo hi | wc -c
385 ## stdout: 3
386 ## status: 0
387
388 #### shift
389 set -- 1 2 3 4
390 shift
391 echo "$@"
392 shift 2
393 echo "$@"
394 ## stdout-json: "2 3 4\n4\n"
395 ## status: 0
396
397 #### Shifting too far
398 set -- 1
399 shift 2
400 ## status: 1
401 ## OK dash status: 2
402
403 #### Invalid shift argument
404 shift ZZZ
405 ## status: 2
406 ## OK bash status: 1
407 ## BUG mksh/zsh status: 0
408
409 #### get umask
410 umask | grep '[0-9]\+' # check for digits
411 ## status: 0
412
413 #### set umask in octal
414 rm -f $TMP/umask-one $TMP/umask-two
415 umask 0002
416 echo one > $TMP/umask-one
417 umask 0022
418 echo two > $TMP/umask-two
419 stat -c '%a' $TMP/umask-one $TMP/umask-two
420 ## status: 0
421 ## stdout-json: "664\n644\n"
422 ## stderr-json: ""
423
424 #### set umask symbolically
425 umask 0002 # begin in a known state for the test
426 rm -f $TMP/umask-one $TMP/umask-two
427 echo one > $TMP/umask-one
428 umask g-w,o-w
429 echo two > $TMP/umask-two
430 stat -c '%a' $TMP/umask-one $TMP/umask-two
431 ## status: 0
432 ## STDOUT:
433 664
434 644
435 ## END
436 ## stderr-json: ""