1 #!/bin/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: huB
19 # OK mksh stdout: ush
20 # N-I dash stdout-json: ""
21 # N-I dash status: 2
22
23 ### sh -c
24 $SH -c 'echo hi'
25 # stdout: hi
26 # status: 0
27
28 ### empty -c input
29 # had a bug here
30 $SH -c ''
31 # stdout-json: ""
32 # status: 0
33
34 ### empty stdin
35 # had a bug here
36 echo -n '' | $SH
37 # stdout-json: ""
38 # status: 0
39
40 ### args are passed
41 $SH -c 'argv.py "$@"' dummy a b
42 # stdout: ['a', 'b']
43
44 ### args that look like flags are passed after script
45 script=$TMP/sh1.sh
46 echo 'argv.py "$@"' > $script
47 chmod +x $script
48 $SH $script --help --help -h
49 # stdout: ['--help', '--help', '-h']
50
51 ### args that look like flags are passed after -c
52 $SH -c 'argv.py "$@"' --help --help -h
53 # stdout: ['--help', '-h']
54
55 ### pass short options on command line
56 $SH -e -c 'false; echo status=$?'
57 # stdout-json: ""
58 # status: 1
59
60 ### pass long options on command line
61 $SH -o errexit -c 'false; echo status=$?'
62 # stdout-json: ""
63 # status: 1
64
65 ### can continue after unknown option
66 # dash and mksh make this a fatal error no matter what.
67 set -o errexit
68 set -o STRICT || true # unknown option
69 echo hello
70 # stdout: hello
71 # status: 0
72 # BUG dash/mksh stdout-json: ""
73 # BUG dash status: 2
74 # BUG mksh status: 1
75
76 ### set with both options and argv
77 set -o errexit a b c
78 echo "$@"
79 false
80 echo done
81 # stdout: a b c
82 # status: 1
83
84 ### nounset
85 echo "[$unset]"
86 set -o nounset
87 echo "[$unset]"
88 echo end # never reached
89 # stdout: []
90 # status: 1
91 # OK dash status: 2
92
93 ### -u is nounset
94 echo "[$unset]"
95 set -u
96 echo "[$unset]"
97 echo end # never reached
98 # stdout: []
99 # status: 1
100 # OK dash status: 2
101
102 ### nounset with "$@"
103 set a b c
104 set -u # shouldn't touch argv
105 echo "$@"
106 # stdout: a b c
107
108 ### set -u -- clears argv
109 set a b c
110 set -u -- # shouldn't touch argv
111 echo "$@"
112 # stdout:
113
114 ### set -u -- x y z
115 set a b c
116 set -u -- x y z
117 echo "$@"
118 # stdout: x y z
119
120 ### reset option with long flag
121 set -o errexit
122 set +o errexit
123 echo "[$unset]"
124 # stdout: []
125 # status: 0
126
127 ### reset option with short flag
128 set -u
129 set +u
130 echo "[$unset]"
131 # stdout: []
132 # status: 0
133
134 ### set -eu (flag parsing)
135 set -eu
136 echo "[$unset]"
137 echo status=$?
138 # stdout-json: ""
139 # status: 1
140 # OK dash status: 2
141
142 ### -n for no execution (useful with --ast-output)
143 # NOTE: set +n doesn't work because nothing is executed!
144 echo 1
145 set -n
146 echo 2
147 set +n
148 echo 3
149 # stdout-json: "1\n"
150 # status: 0
151
152 ### xtrace
153 echo 1
154 set -o xtrace
155 echo 2
156 # stdout-json: "1\n2\n"
157 # stderr: + echo 2
158
159 ### errexit aborts early
160 set -o errexit
161 false
162 echo done
163 # stdout-json: ""
164 # status: 1
165
166 ### errexit for nonexistent command
167 set -o errexit
168 nonexistent__ZZ
169 echo done
170 # stdout-json: ""
171 # status: 127
172
173 ### errexit aborts early on pipeline
174 set -o errexit
175 echo hi | grep nonexistent
176 echo two
177 # stdout-json: ""
178 # status: 1
179
180 ### errexit with { }
181 # This aborts because it's not part of an if statement.
182 set -o errexit
183 { echo one; false; echo two; }
184 # stdout: one
185 # status: 1
186
187 ### errexit with if and { }
188 set -o errexit
189 if { echo one; false; echo two; }; then
190 echo three
191 fi
192 echo four
193 # stdout-json: "one\ntwo\nthree\nfour\n"
194 # status: 0
195
196 ### errexit with ||
197 set -o errexit
198 echo hi | grep nonexistent || echo ok
199 # stdout: ok
200 # status: 0
201
202 ### errexit with &&
203 set -o errexit
204 echo ok && echo hi | grep nonexistent
205 # stdout: ok
206 # status: 1
207
208 ### errexit with !
209 set -o errexit
210 echo one
211 ! true
212 echo two
213 ! false
214 echo three
215 # stdout-json: "one\ntwo\nthree\n"
216 # status: 0
217
218 ### errexit with while/until
219 set -o errexit
220 while false; do
221 echo ok
222 done
223 until false; do
224 echo ok # do this once then exit loop
225 break
226 done
227 # stdout: ok
228 # status: 0
229
230 ### errexit with (( ))
231 # from http://mywiki.wooledge.org/BashFAQ/105, this changed between verisons.
232 set -o errexit
233 i=0
234 (( i++ ))
235 echo done
236 # stdout-json: ""
237 # status: 1
238 # N-I dash status: 127
239 # N-I dash stdout-json: ""
240
241 ### errexit with subshell
242 set -o errexit
243 ( echo one; false; echo two; )
244 # stdout: one
245 # status: 1
246
247 ### errexit with command sub
248 # This is the bash-specific bug here:
249 # https://blogs.janestreet.com/when-bash-scripts-bite/
250 set -o errexit
251 s=$(echo one; false; echo two;)
252 echo "$s"
253 # stdout-json: ""
254 # status: 1
255 # BUG bash status: 0
256 # BUG bash stdout-json: "one\ntwo\n"
257
258 ### errexit with local
259 # I've run into this problem a lot.
260 # https://blogs.janestreet.com/when-bash-scripts-bite/
261 set -o errexit
262 f() {
263 echo good
264 local x=$(echo bad; false)
265 echo $x
266 }
267 f
268 # stdout-json: "good\n"
269 # status: 1
270 # BUG bash/dash/mksh stdout-json: "good\nbad\n"
271 # BUG bash/dash/mksh status: 0
272
273 ### setting errexit while it's being ignored
274 # ignored and then set again
275 set -o errexit
276 # osh aborts early here
277 if { echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; }; then
278 echo 5;
279 fi
280 echo 6
281 false # this is the one that makes other shells fail
282 echo 7
283 # status: 1
284 # stdout-json: "1\n2\n"
285 # OK dash/bash/mksh stdout-json: "1\n2\n3\n4\n5\n6\n"
286
287 ### setting errexit in a subshell works but doesn't affect parent shell
288 ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; )
289 echo 5
290 false
291 echo 6
292 # stdout-json: "1\n2\n3\n5\n6\n"
293 # status: 0
294
295 ### setting errexit while it's being ignored in a subshell
296 set -o errexit
297 if ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4 ); then
298 echo 5;
299 fi
300 echo 6 # This is executed because the subshell just returns false
301 false
302 echo 7
303 # status: 1
304 # stdout-json: "1\n2\n6\n"
305 # OK dash/bash/mksh stdout-json: "1\n2\n3\n4\n5\n6\n"
306
307 ### errexit double quard
308 # OSH bug fix. ErrExit needs a counter, not a boolean.
309 set -o errexit
310 if { ! false; false; true; } then
311 echo true
312 fi
313 false
314 echo done
315 # status: 1
316 # stdout-json: "true\n"
317
318 ### pipefail
319 # NOTE: the sleeps are because osh can fail non-deterministically because of a
320 # bug. Same problem as PIPESTATUS.
321 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; }
322 echo $?
323 set -o pipefail
324 { sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; }
325 echo $?
326 # stdout-json: "0\n2\n"
327 # status: 0
328 # N-I dash stdout-json: "0\n"
329 # N-I dash status: 2