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