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: 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 test && -- from gen-module-init |
209 |
set -o errexit |
210 |
test "$mod" = readline && echo "#endif" |
211 |
echo status=$? |
212 |
# stdout: status=1 |
213 |
|
214 |
### errexit test && and fail |
215 |
set -o errexit |
216 |
test -n X && false |
217 |
echo status=$? |
218 |
# stdout-json: "" |
219 |
# status: 1 |
220 |
|
221 |
### errexit and loop |
222 |
set -o errexit |
223 |
for x in 1 2 3; do |
224 |
test $x = 2 && echo "hi $x" |
225 |
done |
226 |
# stdout: hi 2 |
227 |
# status: 1 |
228 |
|
229 |
### errexit and brace group { } |
230 |
set -o errexit |
231 |
{ test no = yes && echo hi; } |
232 |
echo status=$? |
233 |
# stdout: status=1 |
234 |
|
235 |
### errexit and time { } |
236 |
set -o errexit |
237 |
time false |
238 |
echo status=$? |
239 |
# status: 1 |
240 |
|
241 |
### errexit with ! |
242 |
set -o errexit |
243 |
echo one |
244 |
! true |
245 |
echo two |
246 |
! false |
247 |
echo three |
248 |
# stdout-json: "one\ntwo\nthree\n" |
249 |
# status: 0 |
250 |
|
251 |
### errexit with ! and ; |
252 |
# AST has extra Sentence nodes; there was a REGRESSION here. |
253 |
set -o errexit; echo one; ! true; echo two; ! false; echo three |
254 |
# stdout-json: "one\ntwo\nthree\n" |
255 |
# status: 0 |
256 |
|
257 |
### errexit with while/until |
258 |
set -o errexit |
259 |
while false; do |
260 |
echo ok |
261 |
done |
262 |
until false; do |
263 |
echo ok # do this once then exit loop |
264 |
break |
265 |
done |
266 |
# stdout: ok |
267 |
# status: 0 |
268 |
|
269 |
### errexit with (( )) |
270 |
# from http://mywiki.wooledge.org/BashFAQ/105, this changed between verisons. |
271 |
set -o errexit |
272 |
i=0 |
273 |
(( i++ )) |
274 |
echo done |
275 |
# stdout-json: "" |
276 |
# status: 1 |
277 |
# N-I dash status: 127 |
278 |
# N-I dash stdout-json: "" |
279 |
|
280 |
### errexit with subshell |
281 |
set -o errexit |
282 |
( echo one; false; echo two; ) |
283 |
# stdout: one |
284 |
# status: 1 |
285 |
|
286 |
### errexit with command sub |
287 |
# This is the bash-specific bug here: |
288 |
# https://blogs.janestreet.com/when-bash-scripts-bite/ |
289 |
set -o errexit |
290 |
s=$(echo one; false; echo two;) |
291 |
echo "$s" |
292 |
# stdout-json: "" |
293 |
# status: 1 |
294 |
# BUG bash status: 0 |
295 |
# BUG bash stdout-json: "one\ntwo\n" |
296 |
|
297 |
### errexit with local |
298 |
# I've run into this problem a lot. |
299 |
# https://blogs.janestreet.com/when-bash-scripts-bite/ |
300 |
set -o errexit |
301 |
f() { |
302 |
echo good |
303 |
local x=$(echo bad; false) |
304 |
echo $x |
305 |
} |
306 |
f |
307 |
# stdout-json: "good\n" |
308 |
# status: 1 |
309 |
# BUG bash/dash/mksh stdout-json: "good\nbad\n" |
310 |
# BUG bash/dash/mksh status: 0 |
311 |
|
312 |
### setting errexit while it's being ignored |
313 |
# ignored and then set again |
314 |
set -o errexit |
315 |
# osh aborts early here |
316 |
if { echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; }; then |
317 |
echo 5; |
318 |
fi |
319 |
echo 6 |
320 |
false # this is the one that makes other shells fail |
321 |
echo 7 |
322 |
# status: 1 |
323 |
# stdout-json: "1\n2\n" |
324 |
# OK dash/bash/mksh stdout-json: "1\n2\n3\n4\n5\n6\n" |
325 |
|
326 |
### setting errexit in a subshell works but doesn't affect parent shell |
327 |
( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; ) |
328 |
echo 5 |
329 |
false |
330 |
echo 6 |
331 |
# stdout-json: "1\n2\n3\n5\n6\n" |
332 |
# status: 0 |
333 |
|
334 |
### setting errexit while it's being ignored in a subshell |
335 |
set -o errexit |
336 |
if ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4 ); then |
337 |
echo 5; |
338 |
fi |
339 |
echo 6 # This is executed because the subshell just returns false |
340 |
false |
341 |
echo 7 |
342 |
# status: 1 |
343 |
# stdout-json: "1\n2\n6\n" |
344 |
# OK dash/bash/mksh stdout-json: "1\n2\n3\n4\n5\n6\n" |
345 |
|
346 |
### errexit double quard |
347 |
# OSH bug fix. ErrExit needs a counter, not a boolean. |
348 |
set -o errexit |
349 |
if { ! false; false; true; } then |
350 |
echo true |
351 |
fi |
352 |
false |
353 |
echo done |
354 |
# status: 1 |
355 |
# stdout-json: "true\n" |
356 |
|
357 |
### pipefail |
358 |
# NOTE: the sleeps are because osh can fail non-deterministically because of a |
359 |
# bug. Same problem as PIPESTATUS. |
360 |
{ sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; } |
361 |
echo $? |
362 |
set -o pipefail |
363 |
{ sleep 0.01; exit 9; } | { sleep 0.02; exit 2; } | { sleep 0.03; exit 0; } |
364 |
echo $? |
365 |
# stdout-json: "0\n2\n" |
366 |
# status: 0 |
367 |
# N-I dash stdout-json: "0\n" |
368 |
# N-I dash status: 2 |
369 |
|
370 |
### shopt -p -o |
371 |
shopt -po nounset |
372 |
set -u |
373 |
shopt -po nounset |
374 |
# stdout-json: "set +o nounset\nset -o nounset\n" |
375 |
# N-I dash/mksh stdout-json: "" |
376 |
# N-I dash/mksh status: 127 |
377 |
|
378 |
### shopt -p |
379 |
shopt -p nullglob |
380 |
shopt -s nullglob |
381 |
shopt -p nullglob |
382 |
# stdout-json: "shopt -u nullglob\nshopt -s nullglob\n" |
383 |
# N-I dash/mksh stdout-json: "" |
384 |
# N-I dash/mksh status: 127 |