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 func() { echo $0 $1 $2 | sed -e 's/.*sh/sh/'; }
51 func a b
52 ## stdout: sh a b
53 ## BUG zsh stdout: func a b
54
55 #### $?
56 echo $? # starts out as 0
57 sh -c 'exit 33'
58 echo $?
59 ## stdout-json: "0\n33\n"
60 ## status: 0
61
62 #### $#
63 set -- 1 2 3 4
64 echo $#
65 ## stdout: 4
66 ## status: 0
67
68 #### $_
69 # This is bash-specific.
70 echo hi
71 echo $_
72 ## stdout-json: "hi\nhi\n"
73 ## N-I dash/mksh stdout-json: "hi\n\n"
74
75 #### $$ looks like a PID
76 # Just test that it has decimal digits
77 echo $$ | egrep '[0-9]+'
78 ## status: 0
79
80 #### $$ doesn't change with subshell or command sub
81 # Just test that it has decimal digits
82 set -o errexit
83 die() {
84 echo 1>&2 "$@"; exit 1
85 }
86 parent=$$
87 test -n "$parent" || die "empty PID in parent"
88 ( child=$$
89 test -n "$child" || die "empty PID in subshell"
90 test "$parent" = "$child" || die "should be equal: $parent != $child"
91 echo 'subshell OK'
92 )
93 echo $( child=$$
94 test -n "$child" || die "empty PID in command sub"
95 test "$parent" = "$child" || die "should be equal: $parent != $child"
96 echo 'command sub OK'
97 )
98 exit 3 # make sure we got here
99 ## status: 3
100 ## STDOUT:
101 subshell OK
102 command sub OK
103 ## END
104
105 #### $BASHPID DOES change with subshell and command sub
106 set -o errexit
107 die() {
108 echo 1>&2 "$@"; exit 1
109 }
110 parent=$BASHPID
111 test -n "$parent" || die "empty BASHPID in parent"
112 ( child=$BASHPID
113 test -n "$child" || die "empty BASHPID in subshell"
114 test "$parent" != "$child" || die "should not be equal: $parent = $child"
115 echo 'subshell OK'
116 )
117 echo $( child=$BASHPID
118 test -n "$child" || die "empty BASHPID in command sub"
119 test "$parent" != "$child" ||
120 die "should not be equal: $parent = $child"
121 echo 'command sub OK'
122 )
123 exit 3 # make sure we got here
124 ## status: 3
125 ## STDOUT:
126 subshell OK
127 command sub OK
128 ## END
129 ## N-I dash/zsh status: 1
130 ## N-I dash/zsh stdout-json: ""
131
132 #### Background PID $! looks like a PID
133 sleep 0.01 &
134 pid=$!
135 wait
136 echo $pid | egrep '[0-9]+' >/dev/null
137 echo status=$?
138 ## stdout: status=0
139
140 #### $PPID
141 echo $PPID | egrep '[0-9]+'
142 ## status: 0
143
144 # NOTE: There is also $BASHPID
145
146 #### $PIPESTATUS
147 echo hi | sh -c 'cat; exit 33' | wc -l >/dev/null
148 argv.py "${PIPESTATUS[@]}"
149 ## status: 0
150 ## STDOUT:
151 ['0', '33', '0']
152 ## END
153 ## N-I dash stdout-json: ""
154 ## N-I dash status: 2
155 ## N-I zsh STDOUT:
156 ['']
157 ## END
158
159 #### $RANDOM
160 expr $0 : '.*/osh$' && exit 99 # Disabled because of spec-runner.sh issue
161 echo $RANDOM | egrep '[0-9]+'
162 ## status: 0
163 ## N-I dash status: 1
164
165 #### $UID and $EUID
166 # These are both bash-specific.
167 set -o errexit
168 echo $UID | egrep -o '[0-9]+' >/dev/null
169 echo $EUID | egrep -o '[0-9]+' >/dev/null
170 echo status=$?
171 ## stdout: status=0
172 ## N-I dash/mksh stdout-json: ""
173 ## N-I dash/mksh status: 1
174
175 #### $OSTYPE is non-empty
176 test -n "$OSTYPE"
177 echo status=$?
178 ## STDOUT:
179 status=0
180 ## END
181 ## N-I dash/mksh STDOUT:
182 status=1
183 ## END
184
185 #### $HOSTNAME
186 test "$HOSTNAME" = "$(hostname)"
187 echo status=$?
188 ## STDOUT:
189 status=0
190 ## END
191 ## N-I dash/mksh/zsh STDOUT:
192 status=1
193 ## END
194
195 #### $LINENO is the current line, not line of function call
196 echo $LINENO # first line
197 g() {
198 argv.py $LINENO # line 3
199 }
200 f() {
201 argv.py $LINENO # line 6
202 g
203 argv.py $LINENO # line 8
204 }
205 f
206 ## STDOUT:
207 1
208 ['6']
209 ['3']
210 ['8']
211 ## END
212 ## BUG zsh STDOUT:
213 1
214 ['1']
215 ['1']
216 ['3']
217 ## END
218 ## BUG dash STDOUT:
219 1
220 ['2']
221 ['2']
222 ['4']
223 ## END
224
225 #### $LINENO for [[
226 echo one
227 [[ $LINENO -eq 2 ]] && echo OK
228 ## STDOUT:
229 one
230 OK
231 ## END
232 ## N-I dash status: 127
233 ## N-I dash stdout: one
234 ## N-I mksh status: 1
235 ## N-I mksh stdout: one
236
237 #### $LINENO for ((
238 echo one
239 (( x = LINENO ))
240 echo $x
241 ## STDOUT:
242 one
243 2
244 ## END
245 ## N-I dash stdout-json: "one\n\n"
246
247 #### $LINENO in for loop
248 # hm bash doesn't take into account the word break. That's OK; we won't either.
249 echo one
250 for x in \
251 $LINENO zzz; do
252 echo $x
253 done
254 ## STDOUT:
255 one
256 2
257 zzz
258 ## END
259 ## OK mksh STDOUT:
260 one
261 1
262 zzz
263 ## END
264
265 #### $LINENO in other for loops
266 set -- a b c
267 for x; do
268 echo $LINENO $x
269 done
270 ## STDOUT:
271 3 a
272 3 b
273 3 c
274 ## END
275
276 #### $LINENO in for (( loop
277 # This is a real edge case that I'm not sure we care about. We would have to
278 # change the span ID inside the loop to make it really correct.
279 echo one
280 for (( i = 0; i < $LINENO; i++ )); do
281 echo $i
282 done
283 ## STDOUT:
284 one
285 0
286 1
287 ## END
288 ## N-I dash stdout: one
289 ## N-I dash status: 2
290 ## BUG mksh stdout: one
291 ## BUG mksh status: 1
292
293 #### $LINENO for assignment
294 a1=$LINENO a2=$LINENO
295 b1=$LINENO b2=$LINENO
296 echo $a1 $a2
297 echo $b1 $b2
298 ## STDOUT:
299 1 1
300 2 2
301 ## END
302