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