1 #!/usr/bin/env bash
2 #
3 # Test set flags, sh flags.
4
5 #### $- with -c
6 # dash's behavior seems most sensible here?
7 $SH -o nounset -c 'echo $-'
8 ## stdout: u
9 ## OK bash stdout: huBc
10 ## OK mksh stdout: uhc
11 ## status: 0
12
13 #### $- with pipefail
14 set -o pipefail -o nounset
15 echo $-
16 ## stdout: u
17 ## status: 0
18 ## OK bash stdout: huBs
19 ## OK mksh stdout: ush
20 ## N-I dash stdout-json: ""
21 ## N-I dash status: 2
22
23 #### $- with interactive shell
24 $SH -c 'echo $-' | grep i || echo FALSE
25 $SH -i -c 'echo $-' | grep -q i && echo TRUE
26 ## STDOUT:
27 FALSE
28 TRUE
29 ## END
30
31 #### sh -c
32 $SH -c 'echo hi'
33 ## stdout: hi
34 ## status: 0
35
36 #### empty -c input
37 # had a bug here
38 $SH -c ''
39 ## stdout-json: ""
40 ## status: 0
41
42 #### empty stdin
43 # had a bug here
44 echo -n '' | $SH
45 ## stdout-json: ""
46 ## status: 0
47
48 #### args are passed
49 $SH -c 'argv.py "$@"' dummy a b
50 ## stdout: ['a', 'b']
51
52 #### args that look like flags are passed after script
53 script=$TMP/sh1.sh
54 echo 'argv.py "$@"' > $script
55 chmod +x $script
56 $SH $script --help --help -h
57 ## stdout: ['--help', '--help', '-h']
58
59 #### args that look like flags are passed after -c
60 $SH -c 'argv.py "$@"' --help --help -h
61 ## stdout: ['--help', '-h']
62
63 #### pass short options on command line
64 $SH -e -c 'false; echo status=$?'
65 ## stdout-json: ""
66 ## status: 1
67
68 #### pass long options on command line
69 $SH -o errexit -c 'false; echo status=$?'
70 ## stdout-json: ""
71 ## status: 1
72
73 #### can continue after unknown option
74 # dash and mksh make this a fatal error no matter what.
75 set -o errexit
76 set -o STRICT || true # unknown option
77 echo hello
78 ## stdout: hello
79 ## status: 0
80 ## BUG dash/mksh stdout-json: ""
81 ## BUG dash status: 2
82 ## BUG mksh status: 1
83
84 #### set with both options and argv
85 set -o errexit a b c
86 echo "$@"
87 false
88 echo done
89 ## stdout: a b c
90 ## status: 1
91
92 #### set -o vi/emacs
93 set -o vi
94 echo $?
95 set -o emacs
96 echo $?
97 ## STDOUT:
98 0
99 0
100 ## END
101
102 #### nounset
103 echo "[$unset]"
104 set -o nounset
105 echo "[$unset]"
106 echo end # never reached
107 ## stdout: []
108 ## status: 1
109 ## OK dash status: 2
110
111 #### -u is nounset
112 echo "[$unset]"
113 set -u
114 echo "[$unset]"
115 echo end # never reached
116 ## stdout: []
117 ## status: 1
118 ## OK dash status: 2
119
120 #### nounset with "$@"
121 set a b c
122 set -u # shouldn't touch argv
123 echo "$@"
124 ## stdout: a b c
125
126 #### set -u -- clears argv
127 set a b c
128 set -u -- # shouldn't touch argv
129 echo "$@"
130 ## stdout:
131
132 #### set -u -- x y z
133 set a b c
134 set -u -- x y z
135 echo "$@"
136 ## stdout: x y z
137
138 #### reset option with long flag
139 set -o errexit
140 set +o errexit
141 echo "[$unset]"
142 ## stdout: []
143 ## status: 0
144
145 #### reset option with short flag
146 set -u
147 set +u
148 echo "[$unset]"
149 ## stdout: []
150 ## status: 0
151
152 #### set -eu (flag parsing)
153 set -eu
154 echo "[$unset]"
155 echo status=$?
156 ## stdout-json: ""
157 ## status: 1
158 ## OK dash status: 2
159
160 #### -n for no execution (useful with --ast-output)
161 # NOTE: set +n doesn't work because nothing is executed!
162 echo 1
163 set -n
164 echo 2
165 set +n
166 echo 3
167 # osh doesn't work because it only checks -n in bin/oil.py?
168 ## STDOUT:
169 1
170 ## END
171 ## status: 0
172
173 #### pipefail
174 # NOTE: the sleeps are because osh can fail non-deterministically because of a
175 # bug. Same problem as PIPESTATUS.
176 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; }
177 echo $?
178 set -o pipefail
179 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; }
180 echo $?
181 ## stdout-json: "0\n2\n"
182 ## status: 0
183 ## N-I dash stdout-json: "0\n"
184 ## N-I dash status: 2
185
186 #### shopt -p -o
187 shopt -po nounset
188 set -u
189 shopt -po nounset
190 ## stdout-json: "set +o nounset\nset -o nounset\n"
191 ## N-I dash/mksh stdout-json: ""
192 ## N-I dash/mksh status: 127
193
194 #### shopt -p
195 shopt -p nullglob
196 shopt -s nullglob
197 shopt -p nullglob
198 ## stdout-json: "shopt -u nullglob\nshopt -s nullglob\n"
199 ## N-I dash/mksh stdout-json: ""
200 ## N-I dash/mksh status: 127
201
202 #### noclobber off
203 set -o errexit
204 echo foo > $TMP/can-clobber
205 set +C
206 echo foo > $TMP/can-clobber
207 set +o noclobber
208 echo foo > $TMP/can-clobber
209 cat $TMP/can-clobber
210 ## stdout: foo
211
212 #### noclobber on
213 # Not implemented yet.
214 rm $TMP/no-clobber
215 set -C
216 echo foo > $TMP/no-clobber
217 echo $?
218 echo foo > $TMP/no-clobber
219 echo $?
220 ## stdout-json: "0\n1\n"
221 ## OK dash stdout-json: "0\n2\n"
222
223 #### SHELLOPTS is updated when options are changed
224 echo $SHELLOPTS | grep -q xtrace
225 echo $?
226 set -x
227 echo $SHELLOPTS | grep -q xtrace
228 echo $?
229 set +x
230 echo $SHELLOPTS | grep -q xtrace
231 echo $?
232 ## stdout-json: "1\n0\n1\n"
233 ## N-I dash/mksh stdout-json: "1\n1\n1\n"
234
235 #### SHELLOPTS is readonly
236 SHELLOPTS=x
237 echo status=$?
238 ## stdout: status=1
239 ## N-I dash/mksh stdout: status=0
240
241 # Setting a readonly variable in osh is a hard failure.
242 ## OK osh status: 1
243 ## OK osh stdout-json: ""
244
245 #### set -o lists options
246 # NOTE: osh doesn't use the same format yet.
247 set -o | grep -o noexec
248 ## STDOUT:
249 noexec
250 ## END
251
252 #### set without args lists variables
253 __GLOBAL=g
254 f() {
255 local __mylocal=L
256 local __OTHERLOCAL=L
257 __GLOBAL=mutated
258 set | grep '^__'
259 }
260 g() {
261 local __var_in_parent_scope=D
262 f
263 }
264 g
265 ## status: 0
266 ## STDOUT:
267 __GLOBAL='mutated'
268 __OTHERLOCAL='L'
269 __mylocal='L'
270 __var_in_parent_scope='D'
271 ## END
272 ## OK bash STDOUT:
273 __GLOBAL=mutated
274 __OTHERLOCAL=L
275 __mylocal=L
276 __var_in_parent_scope=D
277 ## END
278 ## OK mksh STDOUT:
279 __GLOBAL=mutated
280 __var_in_parent_scope=D
281 __OTHERLOCAL=L
282 __mylocal=L
283 ## END
284
285 #### 'set' and 'eval' round trip
286
287 # NOTE: not testing arrays and associative arrays!
288 _space='[ ]'
289 _whitespace=$'[\t\r\n]'
290 _sq="'single quotes'"
291 _backslash_dq="\\ \""
292 _unicode=$'[\u03bc]'
293
294 # Save the variables
295 varfile=$TMP/vars-$(basename $SH).txt
296
297 set | grep '^_' > "$varfile"
298
299 # Unset variables
300 unset _space _whitespace _sq _backslash_dq _unicode
301 echo [ $_space $_whitespace $_sq $_backslash_dq $_unicode ]
302
303 # Restore them
304
305 . $varfile
306 echo "Code saved to $varfile" 1>&2 # for debugging
307
308 test "$_space" = '[ ]' && echo OK
309 test "$_whitespace" = $'[\t\r\n]' && echo OK
310 test "$_sq" = "'single quotes'" && echo OK
311 test "$_backslash_dq" = "\\ \"" && echo OK
312 test "$_unicode" = $'[\u03bc]' && echo OK
313
314 ## STDOUT:
315 [ ]
316 OK
317 OK
318 OK
319 OK
320 OK
321 ## END
322
323 #### set without args and array variables (not in OSH)
324 declare -a __array
325 __array=(1 2 '3 4')
326 set | grep '^__'
327 ## STDOUT:
328 __array=([0]="1" [1]="2" [2]="3 4")
329 ## END
330 ## OK mksh STDOUT:
331 __array[0]=1
332 __array[1]=2
333 __array[2]='3 4'
334 ## N-I dash stdout-json: ""
335 ## N-I dash status: 2
336 ## N-I osh stdout-json: ""
337 ## N-I osh status: 1
338
339 #### set without args and assoc array variables (not in OSH)
340 typeset -A __assoc
341 __assoc['k e y']='v a l'
342 __assoc[a]=b
343 set | grep '^__'
344 ## STDOUT:
345 __assoc=(["k e y"]="v a l" [a]="b" )
346 ## END
347 ## N-I mksh stdout-json: ""
348 ## N-I mksh status: 1
349 ## N-I dash stdout-json: ""
350 ## N-I dash status: 1
351 ## N-I osh stdout-json: ""
352 ## N-I osh status: 1
353
354 #### shopt -q
355 shopt -q nullglob
356 echo nullglob=$?
357
358 # set it
359 shopt -s nullglob
360
361 shopt -q nullglob
362 echo nullglob=$?
363
364 shopt -q nullglob failglob
365 echo nullglob,failglob=$?
366
367 # set it
368 shopt -s failglob
369 shopt -q nullglob failglob
370 echo nullglob,failglob=$?
371
372 ## STDOUT:
373 nullglob=1
374 nullglob=0
375 nullglob,failglob=1
376 nullglob,failglob=0
377 ## END
378 ## N-I dash/mksh STDOUT:
379 nullglob=127
380 nullglob=127
381 nullglob,failglob=127
382 nullglob,failglob=127
383 ## END
384
385 #### shopt -q invalid
386 shopt -q invalidZZ
387 echo invalidZZ=$?
388 ## STDOUT:
389 invalidZZ=2
390 ## END
391 ## OK bash STDOUT:
392 invalidZZ=1
393 ## END
394 ## N-I dash/mksh STDOUT:
395 invalidZZ=127
396 ## END
397
398 #### sh -i loads rcfile (when combined with -c)
399 $SH -c 'echo 1'
400 cat >$TMP/rcfile <<EOF
401 echo RCFILE
402 EOF
403 $SH --rcfile $TMP/rcfile -i -c 'echo 2'
404 ## STDOUT:
405 1
406 RCFILE
407 2
408 ## END
409 ## N-I dash/mksh STDOUT:
410 1
411 ## END
412 ## N-I dash status: 2
413 ## N-I mksh status: 1