[Tutorial Science] How to write a C program with command line parameter parsing?

How to write a C program with command line parameter parsing?

At work recently, I encountered such a problem: I need to write a program in C language. This program requires command line input. I have learned some knowledge in this area before. This article will take you to sort it out. I hope it will be helpful to you. .

1 written in front

At work recently, I encountered such a problem: I need to write a program in C language. This program requires command line input. I have learned some knowledge in this area before. This article will take you to sort it out. I hope it will be helpful to you. .

2 Demand Analysis

As mentioned above, the specific functional requirements are as follows:

比如:
命令行输入 
    ./test -i in.bin -o out.bin -f -v2
或者
    ./test -i in.bin -o out.bin -f -v
表示的含义是: 执行test程序,输入一个in.bin文件,输出一个out.bin文件,-t表示强制执行(忽略错误), -v2表示使用第1版本功能,如果没有2只有-v,则使用默认的第1版本。

The entire functional requirement is relatively simple, and the core requirement is to be able to filter out the input file name and output file name from the command line parameters, and whether to enforce it (ignoring errors); at the same time, identify the version number used.

3 programming implementation

Next, we consider using C language to realize the above simple requirements under the Linux platform.

3.1 Another way to write the main function

In some C language textbooks, it may only tell you that the prototype of the main function is:

int main(void);

But it didn't tell you, in fact, it has another way of writing:

int main(int argc, const char *argv[]);

If you are a careful programmer, you will definitely find that when there are no command line parameters, the prototypes of the two main functions can run out of the expected effect.

However, if it is like the requirements in this topic, you have to use the second prototype interface to play, because the parameter list you input from the running command line will be reflected by the argc and argv of the main function.

3.2 A rough way to achieve

Let's implement it in a rough way first: directly traverse the argv list and judge one by one. The sample code is as follows:

#include <stdio.h>
#include <string.h>
 #include <stdlib.h>

int main(int argc, char *argv[])
{
    
    
	int i = 0;
	char *in = NULL;
	char *out = NULL;
	int is_force = 0;
	int version_n = 1;
	int is_found_in = 0;
	int is_found_out = 0;

	for (i = 0; i < argc; i++) {
    
    
		//show all input param
		printf("param %d: %s\n", i + 1, argv[i]);

		if (!strcmp(argv[i], "-i")) {
    
    
			is_found_in = 1;
			continue;
		} else if (!strcmp(argv[i], "-o")) {
    
    
			is_found_out = 1;
			continue;
		} else if (!strcmp(argv[i], "-f")) {
    
    
			is_force = 1;
			printf("is_force: %d\n", is_force);
		} else if (!strncmp(argv[i], "-v", 2)) {
    
    
			if (strlen(argv[i]) != 2) {
    
    
				version_n = atoi(argv[i] + 2);
			}
			printf("version_n: %d\n", version_n);
		}

		if (is_found_in) {
    
    
			in = argv[i];
			printf("in -> %s\n", argv[i]);
			is_found_in = 0;
		}

		if (is_found_out) {
    
    
			out = argv[i];
			printf("out -> %s\n", argv[i]);
			is_found_out = 0;
		}
	}

	return 0;
}

After compiling with gcc, the running results are as follows:

./test -i in.bin -o out.bin -f -v2
param 1: ./test
param 2: -i
param 3: in.bin
in -> in.bin
param 4: -o
param 5: out.bin
out -> out.bin
param 6: -f
is_force: 1
param 7: -v2
version_n: 2

./test -i in.bin -o out.bin -f -v
param 1: ./test
param 2: -i
param 3: in.bin
in -> in.bin
param 4: -o
param 5: out.bin
out -> out.bin
param 6: -f
is_force: 1
param 7: -v
version_n: 1

Basically meet expectations.

3.3 A more elegant way to achieve

The above aspects are too rough, is there an elegant way, of course, let’s learn about the getopt function.

Take a look at man:

GETOPT(1)                                                                   User Commands                                                                   GETOPT(1)

NAME
       getopt - parse command options (enhanced)

SYNOPSIS
       getopt optstring parameters
       getopt [options] [--] optstring parameters
       getopt [options] -o|--options optstring [options] [--] parameters

DESCRIPTION
       getopt  is  used to break up (parse) options in command lines for easy parsing by shell procedures, and to check for valid options.  It uses the GNU getopt(3)
       routines to do this.

       The parameters getopt is called with can be divided into two parts: options which modify the way getopt will do the parsing (the options and the optstring  in
       the  SYNOPSIS),  and the parameters which are to be parsed (parameters in the SYNOPSIS).  The second part will start at the first non-option parameter that is
       not an option argument, or after the first occurrence of '--'.  If no '-o' or '--options' option is found in the first part, the first parameter of the second
       part is used as the short options string.

       If  the  environment  variable GETOPT_COMPATIBLE is set, or if the first parameter is not an option (does not start with a '-', the first format in the SYNOP‐
       SIS), getopt will generate output that is compatible with that of other versions of getopt(1).  It will still do parameter shuffling  and  recognize  optional
       arguments (see section COMPATIBILITY for more information).

       Traditional  implementations of getopt(1) are unable to cope with whitespace and other (shell-specific) special characters in arguments and non-option parame‐
       ters.  To solve this problem, this implementation can generate quoted output which must once again be interpreted by the shell (usually by using the eval com‐
       mand).   This has the effect of preserving those characters, but you must call getopt in a way that is no longer compatible with other versions (the second or
       third format in the SYNOPSIS).  To determine whether this enhanced version of getopt(1) is installed, a special test option (-T) can be used.

From here we can see that it was born for parsing command line parameters.

Let's try to use it to realize the functional requirements in this routine:

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

int main(int argc, char *argv[]) 
{
    
    
    int opt;
    char *in = NULL;
    char *out = NULL;
    int is_force = 0;
    int version_n = 1;

    while ((opt = getopt(argc, argv, "i:o:fv::")) != -1) {
    
    
        switch (opt) {
    
    
            case 'i':
                in = optarg;
                printf("Option i was selected, in: %s\n", in);
                break;

            case 'o':
                out = optarg;
                printf("Option b was selected, out: %s\n", out);
                break;

            case 'f':
                is_force = 1;
                printf("Option b was selected, force: %d\n", is_force);
                break;

            case 'v':
                if (!optarg) {
    
    
                    printf("Option c was selected with default value %d\n", version_n);
                } else {
    
    
                    printf("Option c was selected with value %d\n", atoi(optarg));
                }                
                break;

            default:
                fprintf(stderr, "Usage: %s [-i in] [-o out] [-f] [ -vn]\n", argv[0]);
                exit(EXIT_FAILURE);
        }
    }

    return 0;
}

Compile and see the debugging results:

./test -i in.bin -o out.bin -v
Option i was selected, in: in.bin
Option b was selected, out: out.bin
Option c was selected with default value 1

./test -i in.bin -o out.bin -v -f
Option i was selected, in: in.bin
Option b was selected, out: out.bin
Option c was selected with default value 1
Option b was selected, force: 1

$./test -i in.bin -o out.bin -v2 -f
Option i was selected, in: in.bin
Option b was selected, out: out.bin
Option c was selected with value 2
Option b was selected, force: 1

Judging from the debugging results, it can basically meet the requirements.

3.4 A better way to achieve

In fact, there is also a getopt_long function that can describe and use command line parameters in more detail. The reference routine is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

int main(int argc, char *argv[]) 
{
    
    
    int opt;
    char *in = NULL;
    char *out = NULL;
    int is_force = 0;
    int version_n = 1;

    struct option long_options[] = {
    
    
        {
    
    "in",      required_argument,      NULL, 'i'},
        {
    
    "out",     required_argument,      NULL, 'o'},
        {
    
    "force",   no_argument,            NULL, 'f'},
        {
    
    "version", optional_argument,      NULL, 'v'},
        
        {
    
    NULL, 0, NULL, 0}
    };

    while ((opt = getopt_long_only(argc, argv, "i:o:fv::", long_options, NULL)) != -1) {
    
    
        switch (opt) {
    
    
            case 'i':
                in = optarg;
                printf("Option i was selected, in: %s\n", in);
                break;

            case 'o':
                out = optarg;
                printf("Option b was selected, out: %s\n", out);
                break;

            case 'f':
                is_force = 1;
                printf("Option b was selected, force: %d\n", is_force);
                break;

            case 'v':
                if (!optarg) {
    
    
                    printf("Option c was selected with default value %d\n", version_n);
                } else {
    
    
                    printf("Option c was selected with value %d\n", atoi(optarg));
                }                
                break;

            default:
                fprintf(stderr, "Usage: %s [-h] [-v] [-f file]\n", argv[0]);
                exit(EXIT_FAILURE);
        }
    }

    return 0;
}

After compiling, the debugging results are as follows:

./test --in xxx.in --out xxx.out --force --version=2
Option i was selected, in: xxx.in
Option b was selected, out: xxx.out
Option b was selected, force: 1
Option c was selected with value 2

./test -i xxx.in -o xxx.out -f -v2
Option i was selected, in: xxx.in
Option b was selected, out: xxx.out
Option b was selected, force: 1
Option c was selected with value 2

It can be seen that short parameters and long parameters are equivalent, but it should be noted that: the long parameter input method of option is –xxx=yy

Otherwise an error will be reported:

./test --in xxx.in --out xxx.out --force --version2
Option i was selected, in: xxx.in
Option b was selected, out: xxx.out
Option b was selected, force: 1
./test: unrecognized option '--version2'
Usage: ./test [-h] [-v] [-f file]

4 Summary of experience

  • Understand that there is another way to write the main function, so as to support the input with command line parameters;
  • Learn to use getopt, you have to do a command line parameter analysis, isn't that so easy?
  • There is also getopt_long to meet the more elegant needs of command line parameters, you can learn more about it.

5 Benefits at the end of the article

Guess you like

Origin blog.csdn.net/szullc/article/details/130415141