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 for [[
229 echo one
230 [[ $LINENO -eq 2 ]] && echo OK
231 ## STDOUT:
232 one
233 OK
234 ## END
235 ## N-I dash status: 127
236 ## N-I dash stdout: one
237 ## N-I mksh status: 1
238 ## N-I mksh stdout: one
239
240 #### $LINENO for ((
241 echo one
242 (( x = LINENO ))
243 echo $x
244 ## STDOUT:
245 one
246 2
247 ## END
248 ## N-I dash stdout-json: "one\n\n"
249
250 #### $LINENO in for loop
251 # hm bash doesn't take into account the word break. That's OK; we won't either.
252 echo one
253 for x in \
254 $LINENO zzz; do
255 echo $x
256 done
257 ## STDOUT:
258 one
259 2
260 zzz
261 ## END
262 ## OK mksh STDOUT:
263 one
264 1
265 zzz
266 ## END
267
268 #### $LINENO in other for loops
269 set -- a b c
270 for x; do
271 echo $LINENO $x
272 done
273 ## STDOUT:
274 3 a
275 3 b
276 3 c
277 ## END
278
279 #### $LINENO in for (( loop
280 # This is a real edge case that I'm not sure we care about. We would have to
281 # change the span ID inside the loop to make it really correct.
282 echo one
283 for (( i = 0; i < $LINENO; i++ )); do
284 echo $i
285 done
286 ## STDOUT:
287 one
288 0
289 1
290 ## END
291 ## N-I dash stdout: one
292 ## N-I dash status: 2
293 ## BUG mksh stdout: one
294 ## BUG mksh status: 1
295
296 #### $LINENO for assignment
297 a1=$LINENO a2=$LINENO
298 b1=$LINENO b2=$LINENO
299 echo $a1 $a2
300 echo $b1 $b2
301 ## STDOUT:
302 1 1
303 2 2
304 ## END
305