OILS / spec / vars-special.test.sh View on Github | oilshell.org

634 lines, 310 significant
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.
19echo $PWD | grep /
20## status: 0
21
22#### $PWD is not only set, but exported
23env | 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
30sh=$(which $SH)
31
32old_path=$PATH
33unset 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
38PATH=$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
44if egrep -q '(^|:)/usr/bin($|:)' path.txt; then
45 echo yes
46fi
47
48# should contain /bin
49if egrep -q '(^|:)/bin($|:)' path.txt ; then
50 echo yes
51fi
52
53## STDOUT:
54yes
55yes
56## END
57
58
59#### $HOME is NOT set
60case $SH in *zsh) echo 'zsh sets HOME'; exit ;; esac
61
62home=$(echo $HOME)
63test "$home" = ""
64echo status=$?
65
66env | grep HOME
67echo status=$?
68
69# not in interactive shell either
70$SH -i -c 'echo $HOME' | grep /
71echo status=$?
72
73## STDOUT:
74status=0
75status=1
76status=1
77## END
78## BUG zsh STDOUT:
79zsh sets HOME
80## END
81
82
83#### $1 .. $9 are scoped, while $0 is not
84fun() {
85 echo $0 | grep -o 'sh'
86 echo $1 $2
87}
88fun a b
89
90## STDOUT:
91sh
92a b
93## END
94## BUG zsh STDOUT:
95a b
96## END
97
98#### $?
99echo $? # starts out as 0
100sh -c 'exit 33'
101echo $?
102## STDOUT:
1030
10433
105## END
106## status: 0
107
108#### $#
109set -- 1 2 3 4
110echo $#
111## stdout: 4
112## status: 0
113
114#### $$ looks like a PID
115# Just test that it has decimal digits
116echo $$ | egrep '[0-9]+'
117## status: 0
118
119#### $$ doesn't change with subshell or command sub
120# Just test that it has decimal digits
121set -o errexit
122die() {
123 echo 1>&2 "$@"; exit 1
124}
125parent=$$
126test -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)
132echo $( 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 )
137exit 3 # make sure we got here
138## status: 3
139## STDOUT:
140subshell OK
141command sub OK
142## END
143
144#### $BASHPID DOES change with subshell and command sub
145set -o errexit
146die() {
147 echo 1>&2 "$@"; exit 1
148}
149parent=$BASHPID
150test -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)
156echo $( 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 )
162exit 3 # make sure we got here
163
164# mksh also implements BASHPID!
165
166## status: 3
167## STDOUT:
168subshell OK
169command 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
175sleep 0.01 &
176pid=$!
177wait
178echo $pid | egrep '[0-9]+' >/dev/null
179echo status=$?
180## stdout: status=0
181
182#### $PPID
183echo $PPID | egrep '[0-9]+'
184## status: 0
185
186# NOTE: There is also $BASHPID
187
188#### $PIPESTATUS
189echo hi | sh -c 'cat; exit 33' | wc -l >/dev/null
190argv.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
202expr $0 : '.*/osh$' && exit 99 # Disabled because of spec-runner.sh issue
203echo $RANDOM | egrep '[0-9]+'
204## status: 0
205## N-I dash status: 1
206
207#### $UID and $EUID
208# These are both bash-specific.
209set -o errexit
210echo $UID | egrep -o '[0-9]+' >/dev/null
211echo $EUID | egrep -o '[0-9]+' >/dev/null
212echo 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
218test -n "$OSTYPE"
219echo status=$?
220## STDOUT:
221status=0
222## END
223## N-I dash/mksh STDOUT:
224status=1
225## END
226
227#### $HOSTNAME
228test "$HOSTNAME" = "$(hostname)"
229echo status=$?
230## STDOUT:
231status=0
232## END
233## N-I dash/mksh/zsh STDOUT:
234status=1
235## END
236
237#### $LINENO is the current line, not line of function call
238echo $LINENO # first line
239g() {
240 argv.py $LINENO # line 3
241}
242f() {
243 argv.py $LINENO # line 6
244 g
245 argv.py $LINENO # line 8
246}
247f
248## STDOUT:
2491
250['6']
251['3']
252['8']
253## END
254## BUG zsh STDOUT:
2551
256['1']
257['1']
258['3']
259## END
260## BUG dash STDOUT:
2611
262['2']
263['2']
264['4']
265## END
266
267#### $LINENO in "bare" redirect arg (bug regression)
268filename=$TMP/bare3
269rm -f $filename
270> $TMP/bare$LINENO
271test -f $filename && echo written
272echo $LINENO
273## STDOUT:
274written
2755
276## END
277## BUG zsh STDOUT:
278## END
279
280#### $LINENO in redirect arg (bug regression)
281filename=$TMP/lineno_regression3
282rm -f $filename
283echo x > $TMP/lineno_regression$LINENO
284test -f $filename && echo written
285echo $LINENO
286## STDOUT:
287written
2885
289## END
290
291#### $LINENO in [[
292echo one
293[[ $LINENO -eq 2 ]] && echo OK
294## STDOUT:
295one
296OK
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 ((
304echo one
305(( x = LINENO ))
306echo $x
307## STDOUT:
308one
3092
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.
315echo one
316for x in \
317 $LINENO zzz; do
318 echo $x
319done
320## STDOUT:
321one
3222
323zzz
324## END
325## OK mksh STDOUT:
326one
3271
328zzz
329## END
330
331#### $LINENO in other for loops
332set -- a b c
333for x; do
334 echo $LINENO $x
335done
336## STDOUT:
3373 a
3383 b
3393 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.
345echo one
346for (( i = 0; i < $LINENO; i++ )); do
347 echo $i
348done
349## STDOUT:
350one
3510
3521
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
360a1=$LINENO a2=$LINENO
361b1=$LINENO b2=$LINENO
362echo $a1 $a2
363echo $b1 $b2
364## STDOUT:
3651 1
3662 2
367## END
368
369#### $LINENO in case
370case $LINENO in
371 1) echo 'got line 1' ;;
372 *) echo line=$LINENO
373esac
374## STDOUT:
375got line 1
376## END
377## BUG mksh STDOUT:
378line=3
379## END
380
381#### $_ with simple command and evaluation
382
383name=world
384echo "hi $name"
385echo "$_"
386## STDOUT:
387hi world
388hi world
389## END
390## N-I dash/mksh STDOUT:
391hi world
392
393## END
394
395#### $_ and ${_}
396case $SH in (dash|mksh) exit ;; esac
397
398_var=value
399
400: 42
401echo $_ $_var ${_}var
402
403: 'foo'"bar"
404echo $_
405
406## STDOUT:
40742 value 42var
408foobar
409## END
410## N-I dash/mksh stdout-json: ""
411
412#### $_ with word splitting
413case $SH in (dash|mksh) exit ;; esac
414
415setopt shwordsplit # for ZSH
416
417x='with spaces'
418: $x
419echo $_
420
421## STDOUT:
422spaces
423## END
424## N-I dash/mksh stdout-json: ""
425
426#### $_ with pipeline and subshell
427case $SH in (dash|mksh) exit ;; esac
428
429shopt -s lastpipe
430
431seq 3 | echo last=$_
432
433echo pipeline=$_
434
435( echo subshell=$_ )
436echo done=$_
437
438## STDOUT:
439last=
440pipeline=last=
441subshell=pipeline=last=
442done=pipeline=last=
443## END
444
445# very weird semantics for zsh!
446## OK zsh STDOUT:
447last=3
448pipeline=last=3
449subshell=
450done=
451## END
452
453## N-I dash/mksh stdout-json: ""
454
455
456#### $_ with && and ||
457case $SH in (dash|mksh) exit ;; esac
458
459echo hi && echo last=$_
460echo and=$_
461
462echo hi || echo last=$_
463echo or=$_
464
465## STDOUT:
466hi
467last=hi
468and=last=hi
469hi
470or=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
480case $SH in (dash|mksh) exit ;; esac
481
482echo simple
483(( a = 2 + 3 ))
484echo "(( $_"
485
486[[ a == *.py ]]
487echo "[[ $_"
488
489## STDOUT:
490simple
491(( simple
492[[ (( simple
493## END
494
495## N-I dash/mksh stdout-json: ""
496
497
498#### $_ with assignments, arrays, etc.
499case $SH in (dash|mksh) exit ;; esac
500
501: foo
502echo "colon [$_]"
503
504s=bar
505echo "bare assign [$_]"
506
507# zsh uses declare; bash uses s=bar
508declare s=bar
509echo "declare [$_]"
510
511# zsh remains s:declare, bash resets it
512a=(1 2)
513echo "array [$_]"
514
515# zsh sets it to declare, bash uses the LHS a
516declare a=(1 2)
517echo "declare array [$_]"
518
519declare -g d=(1 2)
520echo "declare flag [$_]"
521
522## STDOUT:
523colon [foo]
524bare assign []
525declare [s=bar]
526array []
527declare array [a]
528declare flag [d]
529## END
530
531## OK zsh STDOUT:
532colon [foo]
533bare assign []
534declare [declare]
535array [declare [declare]]
536declare array [declare]
537declare flag [-g]
538## END
539
540## OK osh STDOUT:
541colon [foo]
542bare assign [colon [foo]]
543declare [bare assign [colon [foo]]]
544array [declare [bare assign [colon [foo]]]]
545declare array [array [declare [bare assign [colon [foo]]]]]
546declare flag [declare array [array [declare [bare assign [colon [foo]]]]]]
547## END
548
549## N-I dash/mksh stdout-json: ""
550
551#### $_ with loop
552
553case $SH in (dash|mksh) exit ;; esac
554
555# zsh resets it when in a loop
556
557echo init
558echo begin=$_
559for x in 1 2 3; do
560 echo prev=$_
561done
562
563## STDOUT:
564init
565begin=init
566prev=begin=init
567prev=prev=begin=init
568prev=prev=prev=begin=init
569## END
570
571## OK zsh STDOUT:
572init
573begin=init
574prev=
575prev=prev=
576prev=prev=prev=
577## END
578## N-I dash/mksh stdout-json: ""
579
580
581#### $_ is not undefined on first use
582set -e
583
584x=$($SH -u -c 'echo prev=$_')
585echo status=$?
586
587# bash and mksh set $_ to $0 at first; zsh is empty
588#echo "$x"
589
590## STDOUT:
591status=0
592## END
593
594## N-I dash status: 2
595## N-I dash stdout-json: ""
596
597#### BASH_VERSION / OILS_VERSION
598case $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 ;;
615esac
616## STDOUT:
617matched=0
618## END
619## N-I dash/mksh/zsh STDOUT:
620no version
621## END
622
623#### $SECONDS
624
625# should be zero seconds
626echo seconds=$SECONDS
627
628## status: 0
629## STDOUT:
630seconds=0
631## END
632## N-I dash STDOUT:
633seconds=
634## END