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