1 ## compare_shells: dash bash mksh zsh ash
2 ## oils_failures_allowed: 3
3 ## oils_cpp_failures_allowed: 3
4
5 #### cd and $PWD
6 cd /
7 echo $PWD
8 ## stdout: /
9
10 #### cd BAD/..
11
12 # Odd divergence in shells: dash and mksh normalize the path and don't check
13 # this error.
14 # TODO: I would like OSH to behave like bash and zsh, but separating chdir_arg
15 # and pwd_arg breaks case 17.
16
17 cd nonexistent_ZZ/..
18 echo status=$?
19 ## STDOUT:
20 status=1
21 ## END
22 ## BUG dash/ash/mksh STDOUT:
23 status=0
24 ## END
25
26 #### cd with 2 or more args - with strict_arg_parse
27
28 shopt -s strict_arg_parse
29
30 mkdir -p foo
31 cd foo
32 echo status=$?
33 cd ..
34 echo status=$?
35
36
37 cd foo bar
38 st=$?
39 if test $st -ne 0; then
40 echo 'failed with multiple args'
41 fi
42
43 ## STDOUT:
44 status=0
45 status=0
46 failed with multiple args
47 ## END
48
49 ## N-I dash/ash STDOUT:
50 status=0
51 status=0
52 ## END
53
54 #### cd with 2 or more args is allowed (strict_arg_parse disabled)
55
56 mkdir -p foo
57 cd foo bar
58
59 ## status: 0
60 ## OK bash/zsh status: 1
61 ## OK mksh status: 2
62
63 #### cd - without OLDPWD
64
65 cd - > /dev/null # silence dash output
66 echo status=$?
67 #pwd
68
69 ## STDOUT:
70 status=1
71 ## END
72
73 ## OK mksh STDOUT:
74 status=2
75 ## END
76
77 ## BUG dash/ash/zsh STDOUT:
78 status=0
79 ## END
80
81 #### $OLDPWD
82 cd /
83 cd $TMP
84 echo "old: $OLDPWD"
85 env | grep OLDPWD # It's EXPORTED too!
86 cd -
87 ## STDOUT:
88 old: /
89 OLDPWD=/
90 /
91 ## END
92 ## BUG mksh STDOUT:
93 old: /
94 /
95 ## END
96 ## BUG zsh STDOUT:
97 old: /
98 OLDPWD=/
99 ## END
100
101 #### pwd
102 cd /
103 pwd
104 ## STDOUT:
105 /
106 ## END
107
108 #### pwd after cd ..
109 dir=$TMP/dir-one/dir-two
110 mkdir -p $dir
111 cd $dir
112 echo $(basename $(pwd))
113 cd ..
114 echo $(basename $(pwd))
115 ## STDOUT:
116 dir-two
117 dir-one
118 ## END
119
120 #### pwd with symlink and -P
121 tmp=$TMP/builtins-pwd-1
122 mkdir -p $tmp/target
123 ln -s -f $tmp/target $tmp/symlink
124
125 cd $tmp/symlink
126
127 echo pwd:
128 basename $(pwd)
129
130 echo pwd -P:
131 basename $(pwd -P)
132
133 ## STDOUT:
134 pwd:
135 symlink
136 pwd -P:
137 target
138 ## END
139
140 #### setting $PWD doesn't affect the value of 'pwd' builtin
141 dir=/tmp/oil-spec-test/pwd
142 mkdir -p $dir
143 cd $dir
144
145 PWD=foo
146 echo before $PWD
147 pwd
148 echo after $PWD
149 ## STDOUT:
150 before foo
151 /tmp/oil-spec-test/pwd
152 after foo
153 ## END
154
155 #### unset PWD; then pwd
156 dir=/tmp/oil-spec-test/pwd
157 mkdir -p $dir
158 cd $dir
159
160 unset PWD
161 echo PWD=$PWD
162 pwd
163 echo PWD=$PWD
164 ## STDOUT:
165 PWD=
166 /tmp/oil-spec-test/pwd
167 PWD=
168 ## END
169
170 #### 'unset PWD; pwd' before any cd (tickles a rare corner case)
171 dir=/tmp/oil-spec-test/pwd-2
172 mkdir -p $dir
173 cd $dir
174
175 # ensure clean shell process state
176 $SH -c 'unset PWD; pwd'
177
178 ## STDOUT:
179 /tmp/oil-spec-test/pwd-2
180 ## END
181
182 #### lie about PWD; pwd before any cd
183 dir=/tmp/oil-spec-test/pwd-3
184 mkdir -p $dir
185 cd $dir
186
187 # ensure clean shell process state
188 $SH -c 'PWD=foo; pwd'
189
190 ## STDOUT:
191 /tmp/oil-spec-test/pwd-3
192 ## END
193
194 #### remove pwd dir
195 dir=/tmp/oil-spec-test/pwd
196 mkdir -p $dir
197 cd $dir
198 pwd
199 rmdir $dir
200 echo status=$?
201 pwd
202 echo status=$?
203 ## STDOUT:
204 /tmp/oil-spec-test/pwd
205 status=0
206 /tmp/oil-spec-test/pwd
207 status=0
208 ## END
209 ## OK mksh STDOUT:
210 /tmp/oil-spec-test/pwd
211 status=0
212 status=1
213 ## END
214
215 #### pwd in symlinked dir on shell initialization
216 tmp=$TMP/builtins-pwd-2
217 mkdir -p $tmp
218 mkdir -p $tmp/target
219 ln -s -f $tmp/target $tmp/symlink
220
221 cd $tmp/symlink
222 $SH -c 'basename $(pwd)'
223 unset PWD
224 $SH -c 'basename $(pwd)'
225
226 ## STDOUT:
227 symlink
228 target
229 ## END
230 ## OK mksh STDOUT:
231 target
232 target
233 ## END
234 ## stderr-json: ""
235
236 #### Test the current directory after 'cd ..' involving symlinks
237 dir=$TMP/symlinktest
238 mkdir -p $dir
239 cd $dir
240 mkdir -p a/b/c
241 mkdir -p a/b/d
242 ln -s -f a/b/c c > /dev/null
243 cd c
244 cd ..
245 # Expecting a c/ (since we are in symlinktest) but osh gives c d (thinks we are
246 # in b/)
247 ls
248 ## STDOUT:
249 a
250 c
251 ## END
252
253 #### cd with no arguments
254 HOME=$TMP/home
255 mkdir -p $HOME
256 cd
257 test $(pwd) = "$HOME" && echo OK
258 ## stdout: OK
259
260 #### cd to nonexistent dir
261 cd /nonexistent/dir
262 echo status=$?
263 ## stdout: status=1
264 ## OK dash/ash/mksh stdout: status=2
265
266 #### cd away from dir that was deleted
267 dir=$TMP/cd-nonexistent
268 mkdir -p $dir
269 cd $dir
270 rmdir $dir
271 cd $TMP
272 echo $(basename $OLDPWD)
273 echo status=$?
274 ## STDOUT:
275 cd-nonexistent
276 status=0
277 ## END
278
279 #### cd permits double bare dash
280 cd -- /
281 echo $PWD
282 ## stdout: /
283
284 #### cd to symlink with -L and -P
285 targ=$TMP/cd-symtarget
286 lnk=$TMP/cd-symlink
287 mkdir -p $targ
288 ln -s $targ $lnk
289
290 # -L behavior is the default
291 cd $lnk
292 test $PWD = "$TMP/cd-symlink" && echo OK
293
294 cd -L $lnk
295 test $PWD = "$TMP/cd-symlink" && echo OK
296
297 cd -P $lnk
298 test $PWD = "$TMP/cd-symtarget" && echo OK || echo $PWD
299 ## STDOUT:
300 OK
301 OK
302 OK
303 ## END
304
305 #### cd to relative path with -L and -P
306 die() { echo "$@"; exit 1; }
307
308 targ=$TMP/cd-symtarget/subdir
309 lnk=$TMP/cd-symlink
310 mkdir -p $targ
311 ln -s $TMP/cd-symtarget $lnk
312
313 # -L behavior is the default
314 cd $lnk/subdir
315 test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
316 cd ..
317 test $PWD = "$TMP/cd-symlink" && echo OK
318
319 cd $lnk/subdir
320 test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
321 cd -L ..
322 test $PWD = "$TMP/cd-symlink" && echo OK
323
324 cd $lnk/subdir
325 test $PWD = "$TMP/cd-symlink/subdir" || die "failed"
326 cd -P ..
327 test $PWD = "$TMP/cd-symtarget" && echo OK || echo $PWD
328 ## STDOUT:
329 OK
330 OK
331 OK
332 ## END
333
334 #### unset PWD; cd /tmp is allowed (regression)
335
336 unset PWD; cd /tmp
337 pwd
338
339 ## STDOUT:
340 /tmp
341 ## END
342
343 #### CDPATH is respected
344
345 mkdir -p /tmp/spam/foo /tmp/eggs/foo
346
347 CDPATH='/tmp/spam:/tmp/eggs'
348
349 cd foo
350 echo status=$?
351 pwd
352
353 ## STDOUT:
354 /tmp/spam/foo
355 status=0
356 /tmp/spam/foo
357 ## END
358
359 # doesn't print the dir
360 ## BUG zsh STDOUT:
361 status=0
362 /tmp/spam/foo
363 ## END
364
365
366 #### Change directory in non-shell parent process (make or Python)
367
368 # inspired by Perl package bug
369
370 old_dir=$(pwd)
371
372 mkdir -p cpan/Encode/Byte
373
374 # Simulate make changing the dir
375 wrapped_chdir() {
376 #set -- $SH -c 'echo BEFORE; pwd; echo CD; cd Byte; echo AFTER; pwd'
377
378 set -- $SH -c 'cd Byte; pwd'
379 # strace comes out the same - one getcwd() and one chdir()
380 #set -- strace -e 'getcwd,chdir' "$@"
381
382 python2 -c '
383 from __future__ import print_function
384 import os, sys, subprocess
385
386 argv = sys.argv[1:]
387 print("Python PWD = %r" % os.getenv("PWD"), file=sys.stderr)
388 print("Python argv = %r" % argv, file=sys.stderr)
389
390 os.chdir("cpan/Encode")
391 subprocess.check_call(argv)
392 ' "$@"
393 }
394
395 #wrapped_chdir
396 new_dir=$(wrapped_chdir)
397
398 #echo $old_dir
399
400 # Make the test insensitive to absolute paths
401 echo "${new_dir##$old_dir}"
402
403 ## STDOUT:
404 /cpan/Encode/Byte
405 ## END
406
407 #### What happens when inherited $PWD and current dir disagree?
408
409 DIR=/tmp/osh-spec-cd
410 mkdir -p $DIR
411 cd $DIR
412
413 old_dir=$(pwd)
414
415 mkdir -p cpan/Encode/Byte
416
417 # Simulate make changing the dir
418 wrapped_chdir() {
419 #set -- $SH -c 'echo BEFORE; pwd; echo CD; cd Byte; echo AFTER; pwd'
420
421 # disagreement before we gert here
422 set -- $SH -c '
423 echo "PWD = $PWD"; pwd
424 cd Byte; echo cd=$?
425 echo "PWD = $PWD"; pwd
426 '
427
428 # strace comes out the same - one getcwd() and one chdir()
429 #set -- strace -e 'getcwd,chdir' "$@"
430
431 python2 -c '
432 from __future__ import print_function
433 import os, sys, subprocess
434
435 argv = sys.argv[1:]
436 print("Python argv = %r" % argv, file=sys.stderr)
437
438 os.chdir("cpan/Encode")
439 print("Python PWD = %r" % os.getenv("PWD"), file=sys.stdout)
440 sys.stdout.flush()
441
442 subprocess.check_call(argv)
443 ' "$@"
444 }
445
446 #unset PWD
447 wrapped_chdir
448
449 ## STDOUT:
450 Python PWD = '/tmp/osh-spec-cd'
451 PWD = /tmp/osh-spec-cd/cpan/Encode
452 /tmp/osh-spec-cd/cpan/Encode
453 cd=0
454 PWD = /tmp/osh-spec-cd/cpan/Encode/Byte
455 /tmp/osh-spec-cd/cpan/Encode/Byte
456 ## END
457
458 ## BUG mksh STDOUT:
459 Python PWD = None
460 PWD = /tmp/osh-spec-cd/cpan/Encode
461 /tmp/osh-spec-cd/cpan/Encode
462 cd=0
463 PWD = /tmp/osh-spec-cd/cpan/Encode/Byte
464 /tmp/osh-spec-cd/cpan/Encode/Byte
465 ## END
466
467 #### Survey of getcwd() syscall
468
469 # This is not that important -- see core/sh_init.py
470 # Instead of verifying that stat('.') == stat(PWD), which is two sycalls,
471 # OSH just calls getcwd() unconditionally.
472
473 # so C++ leak sanitizer doesn't print to stderr
474 export ASAN_OPTIONS='detect_leaks=0'
475
476 strace -e getcwd -- $SH -c 'echo hi; pwd; echo $PWD' 1> /dev/null 2> err.txt
477
478 wc -l err.txt
479 #cat err.txt
480
481 ## STDOUT:
482 1 err.txt
483 ## END
484 ## BUG mksh STDOUT:
485 2 err.txt
486 ## END
487
488 #### chdir is a synonym for cd - busybox ash
489
490 chdir /tmp
491
492 if test $? -ne 0; then
493 echo fail
494 exit
495 fi
496
497 pwd
498
499 # It's the same with no args, but mksh fails because of $HOME
500 #chdir
501 #echo status=$?
502
503 ## STDOUT:
504 /tmp
505 ## END
506
507 ## N-I bash STDOUT:
508 fail
509 ## END