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