1 #
2 # NOTE:
3 # - $! is tested in background.test.sh
4 # - $- is tested in sh-options
5 #
6 # TODO: It would be nice to make a table, like:
7 #
8 # $$ $BASHPID $PPID $SHLVL $BASH_SUBSHELL
9 # X
10 # (Subshell, Command Sub, Pipeline, Spawn $0)
11 #
12 # And see whether the variable changed.
13
14 #### $PWD is set
15 # Just test that it has a slash for now.
16 echo $PWD | grep /
17 ## status: 0
18
19 #### $PWD is not only set, but exported
20 env | grep PWD
21 ## status: 0
22 ## BUG mksh status: 1
23
24 #### $PATH is set if unset at startup
25
26 # Get absolute path before changing PATH
27 sh=$(which $SH)
28
29 old_path=$PATH
30 unset PATH
31
32 $sh -c 'echo $PATH' > path.txt
33
34 PATH=$old_path
35
36 # looks like PATH=/usr/bin:/bin for mksh, but more complicated for others
37 # cat path.txt
38
39 # should contain /usr/bin
40 if egrep -q '(^|:)/usr/bin($|:)' path.txt; then
41 echo yes
42 fi
43
44 # should contain /bin
45 if egrep -q '(^|:)/bin($|:)' path.txt ; then
46 echo yes
47 fi
48
49 ## STDOUT:
50 yes
51 yes
52 ## END
53
54
55 #### $HOME is NOT set
56 case $SH in *zsh) echo 'zsh sets HOME'; exit ;; esac
57
58 home=$(echo $HOME)
59 test "$home" = ""
60 echo status=$?
61
62 env | grep HOME
63 echo status=$?
64
65 # not in interactive shell either
66 $SH -i -c 'echo $HOME' | grep /
67 echo status=$?
68
69 ## STDOUT:
70 status=0
71 status=1
72 status=1
73 ## END
74 ## BUG zsh STDOUT:
75 zsh sets HOME
76 ## END
77
78
79 #### $1 .. $9 are scoped, while $0 is not
80 fun() { echo $0 $1 $2 | sed -e 's/.*sh/sh/'; }
81 fun a b
82 ## stdout: sh a b
83 ## BUG zsh stdout: fun a b
84
85 #### $?
86 echo $? # starts out as 0
87 sh -c 'exit 33'
88 echo $?
89 ## STDOUT:
90 0
91 33
92 ## END
93 ## status: 0
94
95 #### $#
96 set -- 1 2 3 4
97 echo $#
98 ## stdout: 4
99 ## status: 0
100
101 #### $_
102 # This is bash-specific.
103 echo hi
104 echo $_
105 ## stdout-json: "hi\nhi\n"
106 ## N-I dash/mksh stdout-json: "hi\n\n"
107
108 #### $$ looks like a PID
109 # Just test that it has decimal digits
110 echo $$ | egrep '[0-9]+'
111 ## status: 0
112
113 #### $$ doesn't change with subshell or command sub
114 # Just test that it has decimal digits
115 set -o errexit
116 die() {
117 echo 1>&2 "$@"; exit 1
118 }
119 parent=$$
120 test -n "$parent" || die "empty PID in parent"
121 ( child=$$
122 test -n "$child" || die "empty PID in subshell"
123 test "$parent" = "$child" || die "should be equal: $parent != $child"
124 echo 'subshell OK'
125 )
126 echo $( child=$$
127 test -n "$child" || die "empty PID in command sub"
128 test "$parent" = "$child" || die "should be equal: $parent != $child"
129 echo 'command sub OK'
130 )
131 exit 3 # make sure we got here
132 ## status: 3
133 ## STDOUT:
134 subshell OK
135 command sub OK
136 ## END
137
138 #### $BASHPID DOES change with subshell and command sub
139 set -o errexit
140 die() {
141 echo 1>&2 "$@"; exit 1
142 }
143 parent=$BASHPID
144 test -n "$parent" || die "empty BASHPID in parent"
145 ( child=$BASHPID
146 test -n "$child" || die "empty BASHPID in subshell"
147 test "$parent" != "$child" || die "should not be equal: $parent = $child"
148 echo 'subshell OK'
149 )
150 echo $( child=$BASHPID
151 test -n "$child" || die "empty BASHPID in command sub"
152 test "$parent" != "$child" ||
153 die "should not be equal: $parent = $child"
154 echo 'command sub OK'
155 )
156 exit 3 # make sure we got here
157
158 # mksh also implements BASHPID!
159
160 ## status: 3
161 ## STDOUT:
162 subshell OK
163 command sub OK
164 ## END
165 ## N-I dash/zsh status: 1
166 ## N-I dash/zsh stdout-json: ""
167
168 #### Background PID $! looks like a PID
169 sleep 0.01 &
170 pid=$!
171 wait
172 echo $pid | egrep '[0-9]+' >/dev/null
173 echo status=$?
174 ## stdout: status=0
175
176 #### $PPID
177 echo $PPID | egrep '[0-9]+'
178 ## status: 0
179
180 # NOTE: There is also $BASHPID
181
182 #### $PIPESTATUS
183 echo hi | sh -c 'cat; exit 33' | wc -l >/dev/null
184 argv.py "${PIPESTATUS[@]}"
185 ## status: 0
186 ## STDOUT:
187 ['0', '33', '0']
188 ## END
189 ## N-I dash stdout-json: ""
190 ## N-I dash status: 2
191 ## N-I zsh STDOUT:
192 ['']
193 ## END
194
195 #### $RANDOM
196 expr $0 : '.*/osh$' && exit 99 # Disabled because of spec-runner.sh issue
197 echo $RANDOM | egrep '[0-9]+'
198 ## status: 0
199 ## N-I dash status: 1
200
201 #### $UID and $EUID
202 # These are both bash-specific.
203 set -o errexit
204 echo $UID | egrep -o '[0-9]+' >/dev/null
205 echo $EUID | egrep -o '[0-9]+' >/dev/null
206 echo status=$?
207 ## stdout: status=0
208 ## N-I dash/mksh stdout-json: ""
209 ## N-I dash/mksh status: 1
210
211 #### $OSTYPE is non-empty
212 test -n "$OSTYPE"
213 echo status=$?
214 ## STDOUT:
215 status=0
216 ## END
217 ## N-I dash/mksh STDOUT:
218 status=1
219 ## END
220
221 #### $HOSTNAME
222 test "$HOSTNAME" = "$(hostname)"
223 echo status=$?
224 ## STDOUT:
225 status=0
226 ## END
227 ## N-I dash/mksh/zsh STDOUT:
228 status=1
229 ## END
230
231 #### $LINENO is the current line, not line of function call
232 echo $LINENO # first line
233 g() {
234 argv.py $LINENO # line 3
235 }
236 f() {
237 argv.py $LINENO # line 6
238 g
239 argv.py $LINENO # line 8
240 }
241 f
242 ## STDOUT:
243 1
244 ['6']
245 ['3']
246 ['8']
247 ## END
248 ## BUG zsh STDOUT:
249 1
250 ['1']
251 ['1']
252 ['3']
253 ## END
254 ## BUG dash STDOUT:
255 1
256 ['2']
257 ['2']
258 ['4']
259 ## END
260
261 #### $LINENO in "bare" redirect arg (bug regression)
262 filename=$TMP/bare3
263 rm -f $filename
264 > $TMP/bare$LINENO
265 test -f $filename && echo written
266 echo $LINENO
267 ## STDOUT:
268 written
269 5
270 ## END
271 ## BUG zsh STDOUT:
272 ## END
273
274 #### $LINENO in redirect arg (bug regression)
275 filename=$TMP/lineno_regression3
276 rm -f $filename
277 echo x > $TMP/lineno_regression$LINENO
278 test -f $filename && echo written
279 echo $LINENO
280 ## STDOUT:
281 written
282 5
283 ## END
284
285 #### $LINENO for [[
286 echo one
287 [[ $LINENO -eq 2 ]] && echo OK
288 ## STDOUT:
289 one
290 OK
291 ## END
292 ## N-I dash status: 127
293 ## N-I dash stdout: one
294 ## N-I mksh status: 1
295 ## N-I mksh stdout: one
296
297 #### $LINENO for ((
298 echo one
299 (( x = LINENO ))
300 echo $x
301 ## STDOUT:
302 one
303 2
304 ## END
305 ## N-I dash stdout-json: "one\n\n"
306
307 #### $LINENO in for loop
308 # hm bash doesn't take into account the word break. That's OK; we won't either.
309 echo one
310 for x in \
311 $LINENO zzz; do
312 echo $x
313 done
314 ## STDOUT:
315 one
316 2
317 zzz
318 ## END
319 ## OK mksh STDOUT:
320 one
321 1
322 zzz
323 ## END
324
325 #### $LINENO in other for loops
326 set -- a b c
327 for x; do
328 echo $LINENO $x
329 done
330 ## STDOUT:
331 3 a
332 3 b
333 3 c
334 ## END
335
336 #### $LINENO in for (( loop
337 # This is a real edge case that I'm not sure we care about. We would have to
338 # change the span ID inside the loop to make it really correct.
339 echo one
340 for (( i = 0; i < $LINENO; i++ )); do
341 echo $i
342 done
343 ## STDOUT:
344 one
345 0
346 1
347 ## END
348 ## N-I dash stdout: one
349 ## N-I dash status: 2
350 ## BUG mksh stdout: one
351 ## BUG mksh status: 1
352
353 #### $LINENO for assignment
354 a1=$LINENO a2=$LINENO
355 b1=$LINENO b2=$LINENO
356 echo $a1 $a2
357 echo $b1 $b2
358 ## STDOUT:
359 1 1
360 2 2
361 ## END
362
363
364 #### $_
365 : 1
366 echo $_
367 : 'foo'"bar"
368 echo $_
369 ## STDOUT:
370 1
371 foobar
372 ## END
373 ## N-I dash/mksh stdout-json: "\n\n"
374
375 #### $_ with assignments, arrays, etc.
376 case $SH in (dash|mksh) exit ;; esac
377
378 : foo
379 echo $_
380
381 s=bar
382 echo s:$_
383
384 # zsh uses declare; bash uses s=bar
385 declare s=bar
386 echo s:$_
387
388 # zsh remains s:declare, bash resets it
389 a=(1 2)
390 echo a:$_
391
392 # zsh sets it to declare, bash uses the LHS a
393 declare a=(1 2)
394 echo a:$_
395
396 declare -g a=(1 2)
397 echo flag:$_
398
399 ## STDOUT:
400 foo
401 s:
402 s:s=bar
403 a:
404 a:a
405 flag:a
406 ## END
407 ## OK zsh STDOUT:
408 foo
409 s:
410 s:declare
411 a:s:declare
412 a:declare
413 flag:-g
414 ## END
415 ## N-I dash/mksh stdout-json: ""
416
417 #### $_ undefined
418 set -e
419
420 # seems to be set to $0 at first, interesting.
421 x=$($SH -u -c 'echo $_')
422 test -n "$x"
423 echo nonempty=$?
424 ## STDOUT:
425 nonempty=0
426 ## END
427 ## OK zsh status: 1
428 ## OK zsh stdout-json: ""
429 ## N-I dash status: 2
430 ## N-I dash stdout-json: ""
431
432 #### BASH_VERSION / OIL_VERSION
433 case $SH in
434 (bash)
435 # BASH_VERSION=zz
436
437 echo $BASH_VERSION | egrep -o '4\.4\.0' > /dev/null
438 echo matched=$?
439 ;;
440 (*osh)
441 # note: version string is mutable like in bash. I guess that's useful for
442 # testing? We might want a strict mode to eliminate that?
443
444 echo $OIL_VERSION | egrep -o '[0-9]\.[0-9]\.' > /dev/null
445 echo matched=$?
446 ;;
447 (*)
448 echo 'no version'
449 ;;
450 esac
451 ## STDOUT:
452 matched=0
453 ## END
454 ## N-I dash/mksh/zsh STDOUT:
455 no version
456 ## END