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

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