1 ## oils_failures_allowed: 2
2 ## compare_shells: dash bash-4.4 mksh zsh
3
4
5 # NOTE:
6 # - $! is tested in background.test.sh
7 # - $- is tested in sh-options
8 #
9 # TODO: It would be nice to make a table, like:
10 #
11 # $$ $BASHPID $PPID $SHLVL $BASH_SUBSHELL
12 # X
13 # (Subshell, Command Sub, Pipeline, Spawn $0)
14 #
15 # And see whether the variable changed.
16
17 #### $PWD is set
18 # Just test that it has a slash for now.
19 echo $PWD | grep /
20 ## status: 0
21
22 #### $PWD is not only set, but exported
23 env | grep PWD
24 ## status: 0
25 ## BUG mksh status: 1
26
27 #### $PATH is set if unset at startup
28
29 # WORKAROUND for Python version of bin/osh -- we can't run bin/oils_for_unix.py
30 # because it a shebang #!/usr/bin/env python2
31 # This test is still useful for the C++ oils-for-unix.
32
33 case $SH in
34 */bin/osh)
35 echo yes
36 echo yes
37 exit
38 ;;
39 esac
40
41 # Get absolute path before changing PATH
42 sh=$(which $SH)
43
44 old_path=$PATH
45 unset PATH
46
47 $sh -c 'echo $PATH' > path.txt
48
49 PATH=$old_path
50
51 # looks like PATH=/usr/bin:/bin for mksh, but more complicated for others
52 # cat path.txt
53
54 # should contain /usr/bin
55 if egrep -q '(^|:)/usr/bin($|:)' path.txt; then
56 echo yes
57 fi
58
59 # should contain /bin
60 if egrep -q '(^|:)/bin($|:)' path.txt ; then
61 echo yes
62 fi
63
64 ## STDOUT:
65 yes
66 yes
67 ## END
68
69
70 #### $HOME is NOT set
71 case $SH in *zsh) echo 'zsh sets HOME'; exit ;; esac
72
73 home=$(echo $HOME)
74 test "$home" = ""
75 echo status=$?
76
77 env | grep HOME
78 echo status=$?
79
80 # not in interactive shell either
81 $SH -i -c 'echo $HOME' | grep /
82 echo status=$?
83
84 ## STDOUT:
85 status=0
86 status=1
87 status=1
88 ## END
89 ## BUG zsh STDOUT:
90 zsh sets HOME
91 ## END
92
93
94 #### $1 .. $9 are scoped, while $0 is not
95 fun() {
96 echo $0 | grep -o 'sh'
97 echo $1 $2
98 }
99 fun a b
100
101 ## STDOUT:
102 sh
103 a b
104 ## END
105 ## BUG zsh STDOUT:
106 a b
107 ## END
108
109 #### $?
110 echo $? # starts out as 0
111 sh -c 'exit 33'
112 echo $?
113 ## STDOUT:
114 0
115 33
116 ## END
117 ## status: 0
118
119 #### $#
120 set -- 1 2 3 4
121 echo $#
122 ## stdout: 4
123 ## status: 0
124
125 #### $$ looks like a PID
126 # Just test that it has decimal digits
127 echo $$ | egrep '[0-9]+'
128 ## status: 0
129
130 #### $$ doesn't change with subshell or command sub
131 # Just test that it has decimal digits
132 set -o errexit
133 die() {
134 echo 1>&2 "$@"; exit 1
135 }
136 parent=$$
137 test -n "$parent" || die "empty PID in parent"
138 ( child=$$
139 test -n "$child" || die "empty PID in subshell"
140 test "$parent" = "$child" || die "should be equal: $parent != $child"
141 echo 'subshell OK'
142 )
143 echo $( child=$$
144 test -n "$child" || die "empty PID in command sub"
145 test "$parent" = "$child" || die "should be equal: $parent != $child"
146 echo 'command sub OK'
147 )
148 exit 3 # make sure we got here
149 ## status: 3
150 ## STDOUT:
151 subshell OK
152 command sub OK
153 ## END
154
155 #### $BASHPID DOES change with subshell and command sub
156 set -o errexit
157 die() {
158 echo 1>&2 "$@"; exit 1
159 }
160 parent=$BASHPID
161 test -n "$parent" || die "empty BASHPID in parent"
162 ( child=$BASHPID
163 test -n "$child" || die "empty BASHPID in subshell"
164 test "$parent" != "$child" || die "should not be equal: $parent = $child"
165 echo 'subshell OK'
166 )
167 echo $( child=$BASHPID
168 test -n "$child" || die "empty BASHPID in command sub"
169 test "$parent" != "$child" ||
170 die "should not be equal: $parent = $child"
171 echo 'command sub OK'
172 )
173 exit 3 # make sure we got here
174
175 # mksh also implements BASHPID!
176
177 ## status: 3
178 ## STDOUT:
179 subshell OK
180 command sub OK
181 ## END
182 ## N-I dash/zsh status: 1
183 ## N-I dash/zsh stdout-json: ""
184
185 #### Background PID $! looks like a PID
186 sleep 0.01 &
187 pid=$!
188 wait
189 echo $pid | egrep '[0-9]+' >/dev/null
190 echo status=$?
191 ## stdout: status=0
192
193 #### $PPID
194 echo $PPID | egrep '[0-9]+'
195 ## status: 0
196
197 # NOTE: There is also $BASHPID
198
199 #### $PIPESTATUS
200 echo hi | sh -c 'cat; exit 33' | wc -l >/dev/null
201 argv.py "${PIPESTATUS[@]}"
202 ## status: 0
203 ## STDOUT:
204 ['0', '33', '0']
205 ## END
206 ## N-I dash stdout-json: ""
207 ## N-I dash status: 2
208 ## N-I zsh STDOUT:
209 ['']
210 ## END
211
212 #### $RANDOM
213 expr $0 : '.*/osh$' && exit 99 # Disabled because of spec-runner.sh issue
214 echo $RANDOM | egrep '[0-9]+'
215 ## status: 0
216 ## N-I dash status: 1
217
218 #### $UID and $EUID
219 # These are both bash-specific.
220 set -o errexit
221 echo $UID | egrep -o '[0-9]+' >/dev/null
222 echo $EUID | egrep -o '[0-9]+' >/dev/null
223 echo status=$?
224 ## stdout: status=0
225 ## N-I dash/mksh stdout-json: ""
226 ## N-I dash/mksh status: 1
227
228 #### $OSTYPE is non-empty
229 test -n "$OSTYPE"
230 echo status=$?
231 ## STDOUT:
232 status=0
233 ## END
234 ## N-I dash/mksh STDOUT:
235 status=1
236 ## END
237
238 #### $HOSTNAME
239 test "$HOSTNAME" = "$(hostname)"
240 echo status=$?
241 ## STDOUT:
242 status=0
243 ## END
244 ## N-I dash/mksh/zsh STDOUT:
245 status=1
246 ## END
247
248 #### $LINENO is the current line, not line of function call
249 echo $LINENO # first line
250 g() {
251 argv.py $LINENO # line 3
252 }
253 f() {
254 argv.py $LINENO # line 6
255 g
256 argv.py $LINENO # line 8
257 }
258 f
259 ## STDOUT:
260 1
261 ['6']
262 ['3']
263 ['8']
264 ## END
265 ## BUG zsh STDOUT:
266 1
267 ['1']
268 ['1']
269 ['3']
270 ## END
271 ## BUG dash STDOUT:
272 1
273 ['2']
274 ['2']
275 ['4']
276 ## END
277
278 #### $LINENO in "bare" redirect arg (bug regression)
279 filename=$TMP/bare3
280 rm -f $filename
281 > $TMP/bare$LINENO
282 test -f $filename && echo written
283 echo $LINENO
284 ## STDOUT:
285 written
286 5
287 ## END
288 ## BUG zsh STDOUT:
289 ## END
290
291 #### $LINENO in redirect arg (bug regression)
292 filename=$TMP/lineno_regression3
293 rm -f $filename
294 echo x > $TMP/lineno_regression$LINENO
295 test -f $filename && echo written
296 echo $LINENO
297 ## STDOUT:
298 written
299 5
300 ## END
301
302 #### $LINENO in [[
303 echo one
304 [[ $LINENO -eq 2 ]] && echo OK
305 ## STDOUT:
306 one
307 OK
308 ## END
309 ## N-I dash status: 127
310 ## N-I dash stdout: one
311 ## N-I mksh status: 1
312 ## N-I mksh stdout: one
313
314 #### $LINENO in ((
315 echo one
316 (( x = LINENO ))
317 echo $x
318 ## STDOUT:
319 one
320 2
321 ## END
322 ## N-I dash stdout-json: "one\n\n"
323
324 #### $LINENO in for loop
325 # hm bash doesn't take into account the word break. That's OK; we won't either.
326 echo one
327 for x in \
328 $LINENO zzz; do
329 echo $x
330 done
331 ## STDOUT:
332 one
333 2
334 zzz
335 ## END
336 ## OK mksh STDOUT:
337 one
338 1
339 zzz
340 ## END
341
342 #### $LINENO in other for loops
343 set -- a b c
344 for x; do
345 echo $LINENO $x
346 done
347 ## STDOUT:
348 3 a
349 3 b
350 3 c
351 ## END
352
353 #### $LINENO in for (( loop
354 # This is a real edge case that I'm not sure we care about. We would have to
355 # change the span ID inside the loop to make it really correct.
356 echo one
357 for (( i = 0; i < $LINENO; i++ )); do
358 echo $i
359 done
360 ## STDOUT:
361 one
362 0
363 1
364 ## END
365 ## N-I dash stdout: one
366 ## N-I dash status: 2
367 ## BUG mksh stdout: one
368 ## BUG mksh status: 1
369
370 #### $LINENO for assignment
371 a1=$LINENO a2=$LINENO
372 b1=$LINENO b2=$LINENO
373 echo $a1 $a2
374 echo $b1 $b2
375 ## STDOUT:
376 1 1
377 2 2
378 ## END
379
380 #### $LINENO in case
381 case $LINENO in
382 1) echo 'got line 1' ;;
383 *) echo line=$LINENO
384 esac
385 ## STDOUT:
386 got line 1
387 ## END
388 ## BUG mksh STDOUT:
389 line=3
390 ## END
391
392 #### $_ with simple command and evaluation
393
394 name=world
395 echo "hi $name"
396 echo "$_"
397 ## STDOUT:
398 hi world
399 hi world
400 ## END
401 ## N-I dash/mksh STDOUT:
402 hi world
403
404 ## END
405
406 #### $_ and ${_}
407 case $SH in (dash|mksh) exit ;; esac
408
409 _var=value
410
411 : 42
412 echo $_ $_var ${_}var
413
414 : 'foo'"bar"
415 echo $_
416
417 ## STDOUT:
418 42 value 42var
419 foobar
420 ## END
421 ## N-I dash/mksh stdout-json: ""
422
423 #### $_ with word splitting
424 case $SH in (dash|mksh) exit ;; esac
425
426 setopt shwordsplit # for ZSH
427
428 x='with spaces'
429 : $x
430 echo $_
431
432 ## STDOUT:
433 spaces
434 ## END
435 ## N-I dash/mksh stdout-json: ""
436
437 #### $_ with pipeline and subshell
438 case $SH in (dash|mksh) exit ;; esac
439
440 shopt -s lastpipe
441
442 seq 3 | echo last=$_
443
444 echo pipeline=$_
445
446 ( echo subshell=$_ )
447 echo done=$_
448
449 ## STDOUT:
450 last=
451 pipeline=last=
452 subshell=pipeline=last=
453 done=pipeline=last=
454 ## END
455
456 # very weird semantics for zsh!
457 ## OK zsh STDOUT:
458 last=3
459 pipeline=last=3
460 subshell=
461 done=
462 ## END
463
464 ## N-I dash/mksh stdout-json: ""
465
466
467 #### $_ with && and ||
468 case $SH in (dash|mksh) exit ;; esac
469
470 echo hi && echo last=$_
471 echo and=$_
472
473 echo hi || echo last=$_
474 echo or=$_
475
476 ## STDOUT:
477 hi
478 last=hi
479 and=last=hi
480 hi
481 or=hi
482 ## END
483
484 ## N-I dash/mksh stdout-json: ""
485
486 #### $_ is not reset with (( and [[
487
488 # bash is inconsistent because it does it for pipelines and assignments, but
489 # not (( and [[
490
491 case $SH in (dash|mksh) exit ;; esac
492
493 echo simple
494 (( a = 2 + 3 ))
495 echo "(( $_"
496
497 [[ a == *.py ]]
498 echo "[[ $_"
499
500 ## STDOUT:
501 simple
502 (( simple
503 [[ (( simple
504 ## END
505
506 ## N-I dash/mksh stdout-json: ""
507
508
509 #### $_ with assignments, arrays, etc.
510 case $SH in (dash|mksh) exit ;; esac
511
512 : foo
513 echo "colon [$_]"
514
515 s=bar
516 echo "bare assign [$_]"
517
518 # zsh uses declare; bash uses s=bar
519 declare s=bar
520 echo "declare [$_]"
521
522 # zsh remains s:declare, bash resets it
523 a=(1 2)
524 echo "array [$_]"
525
526 # zsh sets it to declare, bash uses the LHS a
527 declare a=(1 2)
528 echo "declare array [$_]"
529
530 declare -g d=(1 2)
531 echo "declare flag [$_]"
532
533 ## STDOUT:
534 colon [foo]
535 bare assign []
536 declare [s=bar]
537 array []
538 declare array [a]
539 declare flag [d]
540 ## END
541
542 ## OK zsh STDOUT:
543 colon [foo]
544 bare assign []
545 declare [declare]
546 array [declare [declare]]
547 declare array [declare]
548 declare flag [-g]
549 ## END
550
551 ## OK osh STDOUT:
552 colon [foo]
553 bare assign [colon [foo]]
554 declare [bare assign [colon [foo]]]
555 array [declare [bare assign [colon [foo]]]]
556 declare array [array [declare [bare assign [colon [foo]]]]]
557 declare flag [declare array [array [declare [bare assign [colon [foo]]]]]]
558 ## END
559
560 ## N-I dash/mksh stdout-json: ""
561
562 #### $_ with loop
563
564 case $SH in (dash|mksh) exit ;; esac
565
566 # zsh resets it when in a loop
567
568 echo init
569 echo begin=$_
570 for x in 1 2 3; do
571 echo prev=$_
572 done
573
574 ## STDOUT:
575 init
576 begin=init
577 prev=begin=init
578 prev=prev=begin=init
579 prev=prev=prev=begin=init
580 ## END
581
582 ## OK zsh STDOUT:
583 init
584 begin=init
585 prev=
586 prev=prev=
587 prev=prev=prev=
588 ## END
589 ## N-I dash/mksh stdout-json: ""
590
591
592 #### $_ is not undefined on first use
593 set -e
594
595 x=$($SH -u -c 'echo prev=$_')
596 echo status=$?
597
598 # bash and mksh set $_ to $0 at first; zsh is empty
599 #echo "$x"
600
601 ## STDOUT:
602 status=0
603 ## END
604
605 ## N-I dash status: 2
606 ## N-I dash stdout-json: ""
607
608 #### BASH_VERSION / OILS_VERSION
609 case $SH in
610 bash*)
611 # BASH_VERSION=zz
612
613 echo $BASH_VERSION | egrep -o '4\.4\.0' > /dev/null
614 echo matched=$?
615 ;;
616 *osh)
617 # note: version string is mutable like in bash. I guess that's useful for
618 # testing? We might want a strict mode to eliminate that?
619
620 echo $OILS_VERSION | egrep -o '[0-9]+\.[0-9]+\.' > /dev/null
621 echo matched=$?
622 ;;
623 *)
624 echo 'no version'
625 ;;
626 esac
627 ## STDOUT:
628 matched=0
629 ## END
630 ## N-I dash/mksh/zsh STDOUT:
631 no version
632 ## END
633
634 #### $SECONDS
635
636 # should be zero seconds
637 echo seconds=$SECONDS
638
639 ## status: 0
640 ## STDOUT:
641 seconds=0
642 ## END
643 ## N-I dash STDOUT:
644 seconds=
645 ## END