Different Behavior on getopt

Backgound

Recently, I started my master’s study on computer science at NYU. Going back to school life is amazing for me, but at meantime I have to deal with assignments and labs. For me, labs are more interesting than lectures. On doing one of my labs, I met a issue related to today’s topic – getopt

getopt is a convinent tool helping us handle the command-line arguments. I also use it to parse arguments for my lab’s program which has 4 arguments: “-a -v file1 file2”. The code is like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <unistd.h>

int main(int argc, char *argv[]){
int c;
while((c = getopt(argc, argv, "va")) != -1) {
switch(c) {
case 'v':
puts("v");
break;
case 'a':
puts("a");
break;
default:
break;
}
}
char *file1 = argv[optind];
puts(file1);
char *file2 = argv[optind + 1];
puts(file2);
return 0;
}

I want all options are to be correct, no matter what order it is in. But when I ran it with my MacBook Pro, I notice that getopt didn’t not work as expected on the circumstance below:

1
2
3
4
5
6
7
# On MacOS
$ a.out -a file1 -v file2

# output
a
file1
-v

I can only get -a option from getopt, then file1 and -v as free arguments from argv by optind pointer. The file2 option is missing.

But, why?

Same Name but Diff Thing

The first thing I thought of is test this code on Ubuntu, and it just ran as I expected (I got all options from getopt).

1
2
3
4
5
6
7
8
# On Ubuntu
$ a.out -a file1 -v file2

# output
a
v
file1
file2

There must be something different between these two. After searching, The Linux Document gave the clear explaination about this:

By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end. Two other modes are also implemented. If the first character of optstring is ‘+’ or the environment variable POSIXLY_CORRECT is set, then option processing stops as soon as a nonoption argument is encountered.

Basically, getopt on Ubuntu is a linux specific version, which is “enhanced”. But on macOS, it is still the BSD/POSIX version. It behaves like the document says that the option processing will stop when the first free argument (the argument not start with ‘-‘) appears. All other arguments after that can only be achieve by argv and optind pointer.