1 #!/usr/bin/env bash
2 #
3 # NOTE:
4 # - $! is tested in background.test.sh
5 # - $- is tested in sh-options
6 #
7 # TODO: It would be nice to make a table, like:
8 #
9 # $$ $BASHPID $PPID $SHLVL $BASH_SUBSHELL
10 # X
11 # (Subshell, Command Sub, Pipeline, Spawn $0)
12 #
13 # And see whether the variable changed.
14
15 #### $PWD is set
16 # Just test that it has a slash for now.
17 echo $PWD | grep /
18 ## status: 0
19
20 #### $PWD is not only set, but exported
21 env | grep PWD
22 ## status: 0
23 ## BUG mksh status: 1
24
25 #### $HOME is NOT set
26 case $SH in *zsh) echo 'zsh sets HOME'; exit ;; esac
27
28 home=$(echo $HOME)
29 test "$home" = ""
30 echo status=$?
31
32 env | grep HOME
33 echo status=$?
34
35 # not in interactive shell either
36 $SH -i -c 'echo $HOME' | grep /
37 echo status=$?
38
39 ## STDOUT:
40 status=0
41 status=1
42 status=1
43 ## END
44 ## BUG zsh STDOUT:
45 zsh sets HOME
46 ## END
47
48
49 #### $1 .. $9 are scoped, while $0 is not
50 fun() { echo $0 $1 $2 | sed -e 's/.*sh/sh/'; }
51 fun a b
52 ## stdout: sh a b
53 ## BUG zsh stdout: fun a b
54
55 #### $?
56 echo $? # starts out as 0
57 sh -c 'exit 33'
58 echo $?
59 ## STDOUT:
60 0
61 33
62 ## END
63 ## status: 0
64
65 #### $#
66 set -- 1 2 3 4
67 echo $#
68 ## stdout: 4
69 ## status: 0
70
71 #### $_
72 # This is bash-specific.
73 echo hi
74 echo $_
75 ## stdout-json: "hi\nhi\n"
76 ## N-I dash/mksh stdout-json: "hi\n\n"
77
78 #### $$ looks like a PID
79 # Just test that it has decimal digits
80 echo $$ | egrep '[0-9]+'
81 ## status: 0
82
83 #### $$ doesn't change with subshell or command sub
84 # Just test that it has decimal digits
85 set -o errexit
86 die() {
87 echo 1>&2 "$@"; exit 1
88 }
89 parent=$$
90 test -n "$parent" || die "empty PID in parent"
91 ( child=$$
92 test -n "$child" || die "empty PID in subshell"
93 test "$parent" = "$child" || die "should be equal: $parent != $child"
94 echo 'subshell OK'
95 )
96 echo $( child=$$
97 test -n "$child" || die "empty PID in command sub"
98 test "$parent" = "$child" || die "should be equal: $parent != $child"
99 echo 'command sub OK'
100 )
101 exit 3 # make sure we got here
102 ## status: 3
103 ## STDOUT:
104 subshell OK
105 command sub OK
106 ## END
107
108 #### $BASHPID DOES change with subshell and command sub
109 set -o errexit
110 die() {
111 echo 1>&2 "$@"; exit 1
112 }
113 parent=$BASHPID
114 test -n "$parent" || die "empty BASHPID in parent"
115 ( child=$BASHPID
116 test -n "$child" || die "empty BASHPID in subshell"
117 test "$parent" != "$child" || die "should not be equal: $parent = $child"
118 echo 'subshell OK'
119 )
120 echo $( child=$BASHPID
121 test -n "$child" || die "empty BASHPID in command sub"
122 test "$parent" != "$child" ||
123 die "should not be equal: $parent = $child"
124 echo 'command sub OK'
125 )
126 exit 3 # make sure we got here
127 ## status: 3
128 ## STDOUT:
129 subshell OK
130 command sub OK
131 ## END
132 ## N-I dash/zsh status: 1
133 ## N-I dash/zsh stdout-json: ""
134
135 #### Background PID $! looks like a PID
136 sleep 0.01 &
137 pid=$!
138 wait
139 echo $pid | egrep '[0-9]+' >/dev/null
140 echo status=$?
141 ## stdout: status=0
142
143 #### $PPID
144 echo $PPID | egrep '[0-9]+'
145 ## status: 0
146
147 # NOTE: There is also $BASHPID
148
149 #### $PIPESTATUS
150 echo hi | sh -c 'cat; exit 33' | wc -l >/dev/null
151 argv.py "${PIPESTATUS[@]}"
152 ## status: 0
153 ## STDOUT:
154 ['0', '33', '0']
155 ## END
156 ## N-I dash stdout-json: ""
157 ## N-I dash status: 2
158 ## N-I zsh STDOUT:
159 ['']
160 ## END
161
162 #### $RANDOM
163 expr $0 : '.*/osh$' && exit 99 # Disabled because of spec-runner.sh issue
164 echo $RANDOM | egrep '[0-9]+'
165 ## status: 0
166 ## N-I dash status: 1
167
168 #### $UID and $EUID
169 # These are both bash-specific.
170 set -o errexit
171 echo $UID | egrep -o '[0-9]+' >/dev/null
172 echo $EUID | egrep -o '[0-9]+' >/dev/null
173 echo status=$?
174 ## stdout: status=0
175 ## N-I dash/mksh stdout-json: ""
176 ## N-I dash/mksh status: 1
177
178 #### $OSTYPE is non-empty
179 test -n "$OSTYPE"
180 echo status=$?
181 ## STDOUT:
182 status=0
183 ## END
184 ## N-I dash/mksh STDOUT:
185 status=1
186 ## END
187
188 #### $HOSTNAME
189 test "$HOSTNAME" = "$(hostname)"
190 echo status=$?
191 ## STDOUT:
192 status=0
193 ## END
194 ## N-I dash/mksh/zsh STDOUT:
195 status=1
196 ## END
197
198 #### $LINENO is the current line, not line of function call
199 echo $LINENO # first line
200 g() {
201 argv.py $LINENO # line 3
202 }
203 f() {
204 argv.py $LINENO # line 6
205 g
206 argv.py $LINENO # line 8
207 }
208 f
209 ## STDOUT:
210 1
211 ['6']
212 ['3']
213 ['8']
214 ## END
215 ## BUG zsh STDOUT:
216 1
217 ['1']
218 ['1']
219 ['3']
220 ## END
221 ## BUG dash STDOUT:
222 1
223 ['2']
224 ['2']
225 ['4']
226 ## END
227
228 #### $LINENO in "bare" redirect arg (bug regression)
229 filename=$TMP/bare3
230 rm -f $filename
231 > $TMP/bare$LINENO
232 test -f $filename && echo written
233 echo $LINENO
234 ## STDOUT:
235 written
236 5
237 ## END
238 ## BUG zsh STDOUT:
239 ## END
240
241 #### $LINENO in redirect arg (bug regression)
242 filename=$TMP/lineno_regression3
243 rm -f $filename
244 echo x > $TMP/lineno_regression$LINENO
245 test -f $filename && echo written
246 echo $LINENO
247 ## STDOUT:
248 written
249 5
250 ## END
251
252 #### $LINENO for [[
253 echo one
254 [[ $LINENO -eq 2 ]] && echo OK
255 ## STDOUT:
256 one
257 OK
258 ## END
259 ## N-I dash status: 127
260 ## N-I dash stdout: one
261 ## N-I mksh status: 1
262 ## N-I mksh stdout: one
263
264 #### $LINENO for ((
265 echo one
266 (( x = LINENO ))
267 echo $x
268 ## STDOUT:
269 one
270 2
271 ## END
272 ## N-I dash stdout-json: "one\n\n"
273
274 #### $LINENO in for loop
275 # hm bash doesn't take into account the word break. That's OK; we won't either.
276 echo one
277 for x in \
278 $LINENO zzz; do
279 echo $x
280 done
281 ## STDOUT:
282 one
283 2
284 zzz
285 ## END
286 ## OK mksh STDOUT:
287 one
288 1
289 zzz
290 ## END
291
292 #### $LINENO in other for loops
293 set -- a b c
294 for x; do
295 echo $LINENO $x
296 done
297 ## STDOUT:
298 3 a
299 3 b
300 3 c
301 ## END
302
303 #### $LINENO in for (( loop
304 # This is a real edge case that I'm not sure we care about. We would have to
305 # change the span ID inside the loop to make it really correct.
306 echo one
307 for (( i = 0; i < $LINENO; i++ )); do
308 echo $i
309 done
310 ## STDOUT:
311 one
312 0
313 1
314 ## END
315 ## N-I dash stdout: one
316 ## N-I dash status: 2
317 ## BUG mksh stdout: one
318 ## BUG mksh status: 1
319
320 #### $LINENO for assignment
321 a1=$LINENO a2=$LINENO
322 b1=$LINENO b2=$LINENO
323 echo $a1 $a2
324 echo $b1 $b2
325 ## STDOUT:
326 1 1
327 2 2
328 ## END
329