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 ## stdout-json: "1\n"
168 ## status: 0
169
170 #### pipefail
171 # NOTE: the sleeps are because osh can fail non-deterministically because of a
172 # bug. Same problem as PIPESTATUS.
173 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; }
174 echo $?
175 set -o pipefail
176 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; }
177 echo $?
178 ## stdout-json: "0\n2\n"
179 ## status: 0
180 ## N-I dash stdout-json: "0\n"
181 ## N-I dash status: 2
182
183 #### shopt -p -o
184 shopt -po nounset
185 set -u
186 shopt -po nounset
187 ## stdout-json: "set +o nounset\nset -o nounset\n"
188 ## N-I dash/mksh stdout-json: ""
189 ## N-I dash/mksh status: 127
190
191 #### shopt -p
192 shopt -p nullglob
193 shopt -s nullglob
194 shopt -p nullglob
195 ## stdout-json: "shopt -u nullglob\nshopt -s nullglob\n"
196 ## N-I dash/mksh stdout-json: ""
197 ## N-I dash/mksh status: 127
198
199 #### noclobber off
200 set -o errexit
201 echo foo > $TMP/can-clobber
202 set +C
203 echo foo > $TMP/can-clobber
204 set +o noclobber
205 echo foo > $TMP/can-clobber
206 cat $TMP/can-clobber
207 ## stdout: foo
208
209 #### noclobber on
210 # Not implemented yet.
211 rm $TMP/no-clobber
212 set -C
213 echo foo > $TMP/no-clobber
214 echo $?
215 echo foo > $TMP/no-clobber
216 echo $?
217 ## stdout-json: "0\n1\n"
218 ## OK dash stdout-json: "0\n2\n"
219
220 #### SHELLOPTS is updated when options are changed
221 echo $SHELLOPTS | grep -q xtrace
222 echo $?
223 set -x
224 echo $SHELLOPTS | grep -q xtrace
225 echo $?
226 set +x
227 echo $SHELLOPTS | grep -q xtrace
228 echo $?
229 ## stdout-json: "1\n0\n1\n"
230 ## N-I dash/mksh stdout-json: "1\n1\n1\n"
231
232 #### SHELLOPTS is readonly
233 SHELLOPTS=x
234 echo status=$?
235 ## stdout: status=1
236 ## N-I dash/mksh stdout: status=0
237
238 #### set -o lists options
239 # NOTE: osh doesn't use the same format yet.
240 set -o | grep -o errexit
241 ## STDOUT:
242 errexit
243 ## END
244
245 #### set without args lists variables
246 __GLOBAL=g
247 f() {
248 local __mylocal=L
249 local __OTHERLOCAL=L
250 __GLOBAL=mutated
251 set | grep '^__'
252 }
253 g() {
254 local __var_in_parent_scope=D
255 f
256 }
257 g
258 ## status: 0
259 ## STDOUT:
260 __GLOBAL='mutated'
261 __OTHERLOCAL='L'
262 __mylocal='L'
263 __var_in_parent_scope='D'
264 ## END
265 ## OK bash STDOUT:
266 __GLOBAL=mutated
267 __OTHERLOCAL=L
268 __mylocal=L
269 __var_in_parent_scope=D
270 ## END
271 ## OK mksh STDOUT:
272 __GLOBAL=mutated
273 __var_in_parent_scope=D
274 __OTHERLOCAL=L
275 __mylocal=L
276 ## END
277
278 #### 'set' and 'eval' round trip
279
280 # NOTE: not testing arrays and associative arrays!
281 _space='[ ]'
282 _whitespace=$'[\t\r\n]'
283 _sq="'single quotes'"
284 _backslash_dq="\\ \""
285 _unicode=$'[\u03bc]'
286
287 # Save the variables
288 varfile=$TMP/vars-$(basename $SH).txt
289
290 set | grep '^_' > "$varfile"
291
292 # Unset variables
293 unset _space _whitespace _sq _backslash_dq _unicode
294 echo [ $_space $_whitespace $_sq $_backslash_dq $_unicode ]
295
296 # Restore them
297
298 . $varfile
299 echo "Code saved to $varfile" 1>&2 # for debugging
300
301 test "$_space" = '[ ]' && echo OK
302 test "$_whitespace" = $'[\t\r\n]' && echo OK
303 test "$_sq" = "'single quotes'" && echo OK
304 test "$_backslash_dq" = "\\ \"" && echo OK
305 test "$_unicode" = $'[\u03bc]' && echo OK
306
307 ## STDOUT:
308 [ ]
309 OK
310 OK
311 OK
312 OK
313 OK
314 ## END
315
316 #### set without args lists array variables
317 declare -a __array
318 __array=(1 2 '3 4')
319 set | grep '^__'
320 ## STDOUT:
321 __array=([0]="1" [1]="2" [2]="3 4")
322 ## END
323 ## OK mksh STDOUT:
324 __array[0]=1
325 __array[1]=2
326 __array[2]='3 4'
327 ## N-I dash stdout-json: ""
328 ## N-I dash status: 2
329
330 #### set without args lists assoc array variables
331 typeset -A __assoc
332 __assoc['k e y']='v a l'
333 __assoc[a]=b
334 set | grep '^__'
335 ## STDOUT:
336 __assoc=(["k e y"]="v a l" [a]="b" )
337 ## END
338 ## N-I mksh stdout-json: ""
339 ## N-I mksh status: 1
340 ## N-I dash stdout-json: ""
341 ## N-I dash status: 1
342
343 #### shopt -q
344 shopt -q nullglob
345 echo nullglob=$?
346
347 # set it
348 shopt -s nullglob
349
350 shopt -q nullglob
351 echo nullglob=$?
352
353 shopt -q nullglob failglob
354 echo nullglob,failglob=$?
355
356 # set it
357 shopt -s failglob
358 shopt -q nullglob failglob
359 echo nullglob,failglob=$?
360
361 ## STDOUT:
362 nullglob=1
363 nullglob=0
364 nullglob,failglob=1
365 nullglob,failglob=0
366 ## END
367 ## N-I dash/mksh STDOUT:
368 nullglob=127
369 nullglob=127
370 nullglob,failglob=127
371 nullglob,failglob=127
372 ## END
373
374 #### shopt -q invalid
375 shopt -q invalidZZ
376 echo invalidZZ=$?
377 ## STDOUT:
378 invalidZZ=2
379 ## END
380 ## OK bash STDOUT:
381 invalidZZ=1
382 ## END
383 ## N-I dash/mksh STDOUT:
384 invalidZZ=127
385 ## END
386
387 #### sh -i loads rcfile (when combined with -c)
388 $SH -c 'echo 1'
389 cat >$TMP/rcfile <<EOF
390 echo RCFILE
391 EOF
392 $SH --rcfile $TMP/rcfile -i -c 'echo 2'
393 ## STDOUT:
394 1
395 RCFILE
396 2
397 ## END
398 ## N-I dash/mksh STDOUT:
399 1
400 ## END
401 ## N-I dash status: 2
402 ## N-I mksh status: 1