简体   繁体   中英

Custom Command Line Parameters

I have a problem with command line parameters. I finished the program so I can start it like this from command line:

program.exe test.txt copy_test.txt

Basically, my program does the following :

  • inputs some text file
  • sorts it and copy to a new text file

BUT (always that but?!), I should start the program from command line like this:

program.exe -input=test.txt -output=copy_test.txt

And I don't know how to do that. I researched, but I didn't find any help :(

Please respond.

#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;

int main ( int argc, char* argv[])
{
 ifstream in(argv[1]);
 ofstream out(argv[2]);
 vector <string> sV;
 string line;
 while (in >> line)
  sV.push_back(line);
 for ( int i = 0; i < sV.size(); i++)
 sort ( sV.begin(), sV.end () );
 for ( int i = 0; i < sV.size(); i++)
 out << sV[i] << endl;
 cin.get();
 return 0;
}

You should parse main 's argv arguments in order to check whether they start by -input , -output etc etc.

Doing this from scratch is a hell, but luckily there are many useful libraries to do this, like boost.program_options

I'm a bit late to the party with this, but I'll provide an updated answer. You can actually get the functionality that you want in C++ with a little work using 'getopt'. Using getopt_long() you can create either single character options (like -c ) or named options (like --input ). You can also use getopt_long_only() which will allow you to pass named options with only a single dash. See for example here or this answer .

Example

Here is an example that should accomplish what you're trying to do:

#include <iostream>
#include <getopt.h>
#include <map>
#include <string>

int main (int argc, char** argv)
{
    // Create the variables to store your parameters
    std::map<std::string, std::string> input_parameters ;
    input_parameters["input"] = "default_in" ;   // Storage for input
    input_parameters["output"] = "default_out" ; // Storage for output

    // Create variables to hold your parameters
    const struct option longopts[] =
    {
        {"input", required_argument, 0, 'i'},
        {"output", required_argument, 0, 'o'},
        {0,0,0,0} // This tells getopt that this is the end
    };

    // Some parameters for getopt_long
    int c(0);

    // Get the options from the command line
    while (c != -1) {
        int option_index(-1) ;

        // Read the next command line option
        // Note here that the ':' after the 'i' and 'o' denotes that
        // it requires an argument
        c = getopt_long(argc, argv, "i:o:", longopts, &option_index) ;

        // If the option is valid, fill the corresponding value
        if ((c>0)&&(option_index>=0)) {
            std::cout << option_index << std::endl;
            input_parameters[longopts[option_index].name] = optarg ;
        }

        switch (c) {
            case 'i':
                // Fill input option
                input_parameters["input"] = optarg ;
            case 'o':
                // Fill output option
                input_parameters["output"] = optarg ;
            case '?':
                // getopt_long printed an error message
                break ;
        }
    }

    std::cout << "input  = " << input_parameters["input"] << std::endl;
    std::cout << "output = " << input_parameters["output"] << std::endl;

    return 0 ;
}

Note that here, you would run this leaving a space between the parameter and the value you want to pass to it. This will produce the following:

$ ./myscript --input inputfile.txt --output outputfile.txt
input  = inputfile.txt
output = outputfile.txt

or

$ ./myscript -i inputfile.txt -o outpufile.txt
input  = inputfile.txt
output = outputfile.txt

You can also use --input and -i interchangeably (similarly with --output and -o ).

Begin shameless plug (my own CLOptions code that is built around getopt)

I was actually a little disgruntled with the amount of work it took to get the full blown functionality of getopt with parameters that could be either boolean, double, int, or string. I also had to create an entirely new implementation in EVERY PROJECT! So, instead I put together a quick class called "CLOptions" so I could #include "CLOptions.h" in my code (everything is in a single file) and now I only need one line to define each additional option. It also creates the -h or -help options for printing help information for you! It includes functionality for getting each parameter as either a bool, double, int, or string depending on how you define each parameter. You can take a look at it on GitHub here with an example of how the above method could be implemented . Note that the class is C++11 and will require the -std=c++11 at compile time (although if someone asks, I can try and code up a C version).

Although I haven't tried them, there's also a host of other command line programs that have been designed by other people to solve this problem (for example options or dropt ). You can probably find them by googling around.

You could eliminate the need for advanced users to have to type unnecessary text each time they run your program and instead offer a -help switch that displays usage.

This is generally the approach most command line programs take, and is the reason why many command line enthusiasts appreciate the simplicity and power of the command line.

Another option would be to offer both methods, as some users may prefer the longer method.

Well, with your new format arguments, you can't just pass them as-is to the stream constructors.

You will have to check if they start with a specific string, such as with strncmp and then pass the address of the relevant bit such as argv[1]+8 for the input type.

Since you're providing --input= type prefixes, you may also want to handle the possibility that they may be in the other order as well.

For example, you can replace:

int main (int argc, char* argv[]) {
    ifstream in(argv[1]);
    ofstream out(argv[2]);

with something like:

int main (int argc, char* argv[]) {
    char *infile = 0;
    char *outfile = 0;
    for (int i = 1; i < argc; i++) {
        if (strncmp (argv[i], "--input=", 8) == 0) {
            infile = argv[i] + 8;
        } else {
            if (strncmp (argv[i], "--output=", 9) == 0) {
                outfile = argv[i] + 9;
            } else {
                std::cerr << "Invalid argument [" << argv[i] << "]" << std::endl;
                return -1;
            }
        }
    }
    if ((infile == 0) || (outfile == 0)) {
        std::cerr << "Need to specify input and output file" << std::endl;
        return -1;
    }

    ifstream in(infile);
    ofstream out(outfile);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM