1 ## compare_shells: dash bash mksh ash
2 ## oils_failures_allowed: 3
3
4 #### getopts empty
5 set --
6 getopts 'a:' opt
7 echo "status=$? opt=$opt OPTARG=$OPTARG"
8 ## stdout: status=1 opt=? OPTARG=
9
10 #### getopts sees unknown arg
11 set -- -Z
12 getopts 'a:' opt
13 echo "status=$? opt=$opt OPTARG=$OPTARG"
14 ## STDOUT:
15 status=0 opt=? OPTARG=
16 ## END
17
18 #### getopts three invocations
19 set -- -h -c foo
20 getopts 'hc:' opt
21 echo status=$? opt=$opt
22 getopts 'hc:' opt
23 echo status=$? opt=$opt
24 getopts 'hc:' opt
25 echo status=$? opt=$opt
26 ## STDOUT:
27 status=0 opt=h
28 status=0 opt=c
29 status=1 opt=?
30 ## END
31
32 #### getopts resets OPTARG
33 set -- -c foo -h
34 getopts 'hc:' opt
35 echo status=$? opt=$opt OPTARG=$OPTARG
36 getopts 'hc:' opt
37 echo status=$? opt=$opt OPTARG=$OPTARG
38 ## STDOUT:
39 status=0 opt=c OPTARG=foo
40 status=0 opt=h OPTARG=
41 ## END
42
43 #### OPTARG is empty (not unset) after parsing a flag doesn't take an arg
44
45 set -u
46 getopts 'ab' name '-a'
47 echo name=$name
48 echo OPTARG=$OPTARG
49
50 ## STDOUT:
51 name=a
52 OPTARG=
53 ## END
54
55 ## BUG bash/mksh status: 1
56 ## BUG bash/mksh STDOUT:
57 name=a
58 ## END
59
60 #### Basic getopts invocation
61 set -- -h -c foo x y z
62 FLAG_h=0
63 FLAG_c=''
64 while getopts "hc:" opt; do
65 case $opt in
66 h) FLAG_h=1 ;;
67 c) FLAG_c="$OPTARG" ;;
68 esac
69 done
70 shift $(( OPTIND - 1 ))
71 echo h=$FLAG_h c=$FLAG_c optind=$OPTIND argv=$@
72 ## stdout: h=1 c=foo optind=4 argv=x y z
73
74 #### getopts with invalid variable name
75 set -- -c foo -h
76 getopts 'hc:' opt-
77 echo status=$? opt=$opt OPTARG=$OPTARG OPTIND=$OPTIND
78 ## stdout: status=2 opt= OPTARG=foo OPTIND=3
79 ## OK bash stdout: status=1 opt= OPTARG=foo OPTIND=3
80 ## OK mksh stdout: status=1 opt= OPTARG= OPTIND=1
81
82 #### getopts with invalid flag
83 set -- -h -x
84 while getopts "hc:" opt; do
85 case $opt in
86 h) FLAG_h=1 ;;
87 c) FLAG_c="$OPTARG" ;;
88 '?') echo ERROR $OPTIND; exit 2; ;;
89 esac
90 done
91 echo status=$?
92 ## stdout: ERROR 3
93 ## status: 2
94
95 #### getopts with with -
96 set -- -h -
97 echo "$@"
98 while getopts "hc:" opt; do
99 case $opt in
100 h) FLAG_h=1 ;;
101 c) FLAG_c="$OPTARG" ;;
102 '?') echo ERROR $OPTIND; exit 2; ;;
103 esac
104 done
105 echo status=$?
106 ## STDOUT:
107 -h -
108 status=0
109 ## END
110
111 #### getopts missing required argument
112 set -- -h -c
113 while getopts "hc:" opt; do
114 case $opt in
115 h) FLAG_h=1 ;;
116 c) FLAG_c="$OPTARG" ;;
117 '?') echo ERROR $OPTIND; exit 2; ;;
118 esac
119 done
120 echo status=$?
121 ## stdout: ERROR 3
122 ## status: 2
123
124 #### getopts doesn't look for flags after args
125 set -- x -h -c y
126 FLAG_h=0
127 FLAG_c=''
128 while getopts "hc:" opt; do
129 case $opt in
130 h) FLAG_h=1 ;;
131 c) FLAG_c="$OPTARG" ;;
132 esac
133 done
134 shift $(( OPTIND - 1 ))
135 echo h=$FLAG_h c=$FLAG_c optind=$OPTIND argv=$@
136 ## stdout: h=0 c= optind=1 argv=x -h -c y
137
138 #### getopts with explicit args
139 # NOTE: Alpine doesn't appear to use this, but bash-completion does.
140 FLAG_h=0
141 FLAG_c=''
142 arg=''
143 set -- A B C
144 while getopts "hc:" opt -h -c foo x y z; do
145 case $opt in
146 h) FLAG_h=1 ;;
147 c) FLAG_c="$OPTARG" ;;
148 esac
149 done
150 echo h=$FLAG_h c=$FLAG_c optind=$OPTIND argv=$@
151 ## STDOUT:
152 h=1 c=foo optind=4 argv=A B C
153 ## END
154
155 #### OPTIND
156 echo $OPTIND
157 ## stdout: 1
158
159 #### OPTIND after multiple getopts with same spec
160 while getopts "hc:" opt; do
161 echo '-'
162 done
163 echo OPTIND=$OPTIND
164
165 set -- -h -c foo x y z
166 while getopts "hc:" opt; do
167 echo '-'
168 done
169 echo OPTIND=$OPTIND
170
171 set --
172 while getopts "hc:" opt; do
173 echo '-'
174 done
175 echo OPTIND=$OPTIND
176
177 ## STDOUT:
178 OPTIND=1
179 -
180 -
181 OPTIND=4
182 OPTIND=1
183 ## END
184 ## BUG mksh STDOUT:
185 OPTIND=1
186 -
187 -
188 OPTIND=4
189 OPTIND=4
190 ## END
191
192 #### OPTIND after multiple getopts with different spec
193 # Wow this is poorly specified! A fundamental design problem with the global
194 # variable OPTIND.
195 set -- -a
196 while getopts "ab:" opt; do
197 echo '.'
198 done
199 echo OPTIND=$OPTIND
200
201 set -- -c -d -e foo
202 while getopts "cde:" opt; do
203 echo '-'
204 done
205 echo OPTIND=$OPTIND
206
207 set -- -f
208 while getopts "f:" opt; do
209 echo '_'
210 done
211 echo OPTIND=$OPTIND
212
213 ## STDOUT:
214 .
215 OPTIND=2
216 -
217 -
218 OPTIND=5
219 OPTIND=2
220 ## END
221 ## BUG ash/dash STDOUT:
222 .
223 OPTIND=2
224 -
225 -
226 -
227 OPTIND=5
228 _
229 OPTIND=2
230 ## END
231 ## BUG mksh STDOUT:
232 .
233 OPTIND=2
234 -
235 -
236 OPTIND=5
237 OPTIND=5
238 ## END
239
240 #### OPTIND narrowed down
241 FLAG_a=
242 FLAG_b=
243 FLAG_c=
244 FLAG_d=
245 FLAG_e=
246 set -- -a
247 while getopts "ab:" opt; do
248 case $opt in
249 a) FLAG_a=1 ;;
250 b) FLAG_b="$OPTARG" ;;
251 esac
252 done
253 # Bash doesn't reset OPTIND! It skips over c! mksh at least warns about this!
254 # You have to reset OPTIND yourself.
255
256 set -- -c -d -e E
257 while getopts "cde:" opt; do
258 case $opt in
259 c) FLAG_c=1 ;;
260 d) FLAG_d=1 ;;
261 e) FLAG_e="$OPTARG" ;;
262 esac
263 done
264
265 echo a=$FLAG_a b=$FLAG_b c=$FLAG_c d=$FLAG_d e=$FLAG_e
266
267 ## STDOUT:
268 a=1 b= c=1 d=1 e=E
269 ## END
270
271 ## BUG bash/mksh STDOUT:
272 a=1 b= c= d=1 e=E
273 ## END
274
275
276 #### Getopts parses the function's arguments
277 FLAG_h=0
278 FLAG_c=''
279 myfunc() {
280 while getopts "hc:" opt; do
281 case $opt in
282 h) FLAG_h=1 ;;
283 c) FLAG_c="$OPTARG" ;;
284 esac
285 done
286 }
287 set -- -h -c foo x y z
288 myfunc -c bar
289 echo h=$FLAG_h c=$FLAG_c opt=$opt optind=$OPTIND argv=$@
290 ## stdout: h=0 c=bar opt=? optind=3 argv=-h -c foo x y z
291
292 #### Local OPTIND
293 # minimal test case extracted from bash-completion
294 min() {
295 local OPTIND=1
296
297 while getopts "n:e:o:i:s" flag "$@"; do
298 echo "loop $OPTIND";
299 done
300 }
301 min -s
302 ## stdout: loop 2
303
304 #### two flags: -ab
305 getopts "ab" opt -ab
306 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
307 getopts "ab" opt -ab
308 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
309 ## STDOUT:
310 OPTIND=1 opt=a OPTARG=
311 OPTIND=2 opt=b OPTARG=
312 ## END
313 ## OK dash/mksh/ash STDOUT:
314 OPTIND=2 opt=a OPTARG=
315 OPTIND=2 opt=b OPTARG=
316 ## END
317
318 #### flag and arg: -c10
319 getopts "c:" opt -c10
320 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
321 getopts "c:" opt -c10
322 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
323 ## STDOUT:
324 OPTIND=2 opt=c OPTARG=10
325 OPTIND=2 opt=? OPTARG=
326 ## END
327 ## BUG dash STDOUT:
328 OPTIND=2 opt=c OPTARG=10
329 OPTIND=2 opt=? OPTARG=10
330 ## END
331
332 #### More Smooshing 1
333 getopts "ab:c:" opt -ab hi -c hello
334 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
335 getopts "ab:c:" opt -ab hi -c hello
336 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
337 getopts "ab:c:" opt -ab hi -c hello
338 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
339 ## STDOUT:
340 OPTIND=1 opt=a OPTARG=
341 OPTIND=3 opt=b OPTARG=hi
342 OPTIND=5 opt=c OPTARG=hello
343 ## END
344 ## OK dash/mksh/ash STDOUT:
345 OPTIND=2 opt=a OPTARG=
346 OPTIND=3 opt=b OPTARG=hi
347 OPTIND=5 opt=c OPTARG=hello
348 ## END
349
350 #### More Smooshing 2
351 getopts "abc:" opt -abc10
352 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
353 getopts "abc:" opt -abc10
354 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
355 getopts "abc:" opt -abc10
356 echo OPTIND=$OPTIND opt=$opt OPTARG=$OPTARG
357 ## STDOUT:
358 OPTIND=1 opt=a OPTARG=
359 OPTIND=1 opt=b OPTARG=
360 OPTIND=2 opt=c OPTARG=10
361 ## END
362 ## OK dash/mksh/ash STDOUT:
363 OPTIND=2 opt=a OPTARG=
364 OPTIND=2 opt=b OPTARG=
365 OPTIND=2 opt=c OPTARG=10
366 ## END
367
368 #### OPTIND should be >= 1 (regression)
369 OPTIND=-1
370 getopts a: foo
371 echo status=$?
372
373 OPTIND=0
374 getopts a: foo
375 echo status=$?
376 ## STDOUT:
377 status=1
378 status=1
379 ## END
380 ## OK dash status: 2
381 ## OK dash stdout-json: ""
382
383
384 #### getopts bug #1523
385
386 $SH $REPO_ROOT/spec/testdata/getopts-1523.sh -abcdef -abcde
387
388 ## status: 1
389 ## STDOUT:
390 opt:a
391 opt:b
392 opt:c arg:def
393 opt:a
394 opt:b
395 opt:c arg:de
396 ## END
397
398 #### More regression for #1523
399
400 $SH $REPO_ROOT/spec/testdata/getopts-1523.sh -abcdef -xyz
401
402 ## status: 1
403 ## STDOUT:
404 opt:a
405 opt:b
406 opt:c arg:def
407 err:?
408 err:?
409 err:?
410 ## END
411
412 #### getopts silent error reporting - invalid option
413 # Leading : in optspec enables silent mode: OPTARG=option char, no error msg
414 set -- -Z
415 getopts ':a:' opt 2>&1
416 echo "status=$? opt=$opt OPTARG=$OPTARG"
417 ## status: 0
418 ## stdout: status=0 opt=? OPTARG=Z
419 ## STDERR:
420 ## END
421
422 #### getopts silent error reporting - missing required argument
423 # Silent mode returns ':' and sets OPTARG to option char
424 set -- -a
425 getopts ':a:' opt 2>&1
426 echo "status=$? opt=$opt OPTARG=$OPTARG"
427 ## status: 0
428 ## stdout: status=0 opt=: OPTARG=a
429 ## STDERR:
430 ## END
431
432 #### getopts normal mode - invalid option (compare with silent)
433 # Normal mode: OPTARG is empty, prints error message
434 set -- -Z
435 getopts 'a:' opt 2>/dev/null
436 echo "status=$? opt=$opt OPTARG=$OPTARG"
437 ## status: 0
438 ## stdout: status=0 opt=? OPTARG=
439
440 #### getopts normal mode - missing required argument (compare with silent)
441 # Normal mode returns '?', OPTARG is empty
442 set -- -a
443 getopts 'a:' opt 2>/dev/null
444 echo "status=$? opt=$opt OPTARG=$OPTARG"
445 ## status: 0
446 ## stdout: status=0 opt=? OPTARG=
447
448 #### getopts handles '--' #2579
449 set -- "-a" "--"
450 while getopts "a" name; do
451 case "$name" in
452 a)
453 echo "a"
454 ;;
455 ?)
456 echo "?"
457 ;;
458 esac
459 done
460 echo "name=$name"
461 echo "$OPTIND"
462 ## STDOUT:
463 a
464 name=?
465 3
466 ## END
467
468 #### getopts leaves all args after '--' as operands #2579
469 set -- "-a" "--" "-c" "operand"
470 while getopts "a" name; do
471 case "$name" in
472 a)
473 echo "a"
474 ;;
475 c)
476 echo "c"
477 ;;
478 ?)
479 echo "?"
480 ;;
481 esac
482 done
483 shift $((OPTIND - 1))
484 echo "$#"
485 echo "$@"
486 ## STDOUT:
487 a
488 2
489 -c operand
490 ## END