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