How to parse command line parameters.

Aug 7, 2009 at 4:25pm
A simple tutorial on command line parameters, as some people may not know how it is done.

-sec "What's the point?"
The point is that with command-line parameters you have no need of a human being to invoke your program -- this way, you can have, for example, a calculator with a GUI. The user opens the GUI and types in some commands. The GUI's executable invokes the calculator, for example, like this: calc add 5 5, calc.exe returns 10. Then the GUI can display "10" in a text box. "But you can just use a separate function" I hear you say. Well yes, in this case, you could just have an "add" function. But what if your user is using a text-only display or has a very old computer? They won't be able to use the program you spent two months designing. In the case of a calculator, you could say "But who cares? There are numerous calculators". True; but for something more complex it would be better to have two executables.

Another advantage is that people don't need your source code to include your file with their project. It would certainly be easier; but unnecessary.

Yet another is that it enables you to write in two different programming languages without the need for embedding. In C++ it is possible to embed languages like ASM or Python (I'm not sure how to embed Python, but there's a asm("ASMCommand"); command), but personally I think it better to create a seperate file, as with ASM it would take too long to type out a large piece of code into the asm() function -- you'd have to put \n\ at the end of every line which would be difficult to read; and also because you can't be sure the person reading your code even knows ASM. It may confuse them. So for readability and practicality it would be better to have two executables. Assembler's excellent execution time would not be so evident in a calculator, but as I say, for large projects it's definately worth having it.

There are more that I won't go into right now.

-sec "So how do you do it?"

It is very easy to use command line parameters once you know how. First of all you must declare main() with the following parameters: (int argc, char* argv[]).
Why argc and argv[]? Well really, you could name them anything -- argc and argv[] are simply naming conventions. But they do have meaning -- argument count and argument vector.

--subsec "Argument Count:"

argc contains the amount of arguments, separated by spaces, that were entered when the program was invoked. Let's say I have written a program to move file at arbitrary location x to location y. Let's say I have compiled and linked it as movefile (.exe on windows). If, for example, I opened up a terminal, and typed "movefile <filename> <frompath> <topath>"; then argc would be 4.

--subsec "Argument Vector:"

argv contains the actual arguments themselves. In argv[0] will always be the command used to invoke the program (thanks, Disch). If, for example, movefile was stored in C:\MyPrograms\ or /home/myprograms, and the current working directory (the folder your terminal is in, e.g. "C:\Users\Name>" or "name@workstation:~/home/name$" you would have to type in "C:\MyPrograms\movefile myfile mypath myotherpath" or "/home/myprograms/movefile myfile mypath myotherpath". That is what argv[0] will contain. argv[1] would be "myfile", etc.

-sec Switches:
Switches are sent to your programs as arguments themselves, but there are certain advantages to using a switch over having plain arguments. One such advantage is that you can figure out where your argument variables are. If you do not have any switches, you either have to rely on the user (big mistake) of the program to know the syntax (which would be incredibly frustrating for them), or use some kind of regular expression, which would be incredibly frustrating for you. So using a switch is definately worth it. With a switch, you can do this: "movefile -f myfile -p mypath -o myotherpath". Then you can easily find out where your arguments are:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (i + 1 != argc) // Check that we haven't finished parsing already
    if (argv[i] == "-f") {
        // We know the next argument *should* be the filename:
        myFile = argv[i + 1];
    } else if (argv[i] == "-p") {
        myPath = argv[i + 1];
    } else if (argv[i] == "-o") {
        myOutPath = argv[i + 1];
    } else {
        std::cout << "Not enough or invalid arguments, please try again.\n";
        Sleep(2000);
                 /*
                  *  Sleep for 2 seconds to allow user (if any) to read above statement. 
                  *  The issue with this is that if we're a backend program to a GUI as mentioned above; 
                  *  that program would also sleep for 2 seconds. Most programs don't
                  *  have this - the console will keep the text there until it scrolls off the page or whatever, so you may aswell leave it out.
                  ***/
        exit(0);
    }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

// When passing char arrays as parameters they must be pointers
int main(int argc, char* argv[]) {
    if (argc < 5) { // Check the value of argc. If not enough parameters have been passed, inform user and exit.
        std::cout << "Usage is -in <infile> -out <outdir>\n"; // Inform the user of how to use the program
        std::cin.get();
        exit(0);
    } else { // if we got enough parameters...
        char* myFile, myPath, myOutPath;
        std::cout << argv[0];
        for (int i = 1; i < argc; i++) { /* We will iterate over argv[] to get the parameters stored inside.
                                          * Note that we're starting on 1 because we don't need to know the 
                                          * path of the program, which is stored in argv[0] */
            if (i + 1 != argc) // Check that we haven't finished parsing already
                if (argv[i] == "-f") {
                    // We know the next argument *should* be the filename:
                    myFile = argv[i + 1];
                } else if (argv[i] == "-p") {
                    myPath = argv[i + 1];
                } else if (argv[i] == "-o") {
                    myOutPath = argv[i + 1];
                } else {
                    std::cout << "Not enough or invalid arguments, please try again.\n";
                    Sleep(2000); 
                    exit(0);
            }
            std::cout << argv[i] << " ";
        }
        //... some more code
        std::cin.get();
        return 0;
    }
}


-sec "Words with a space:"
Where arguments or paths with a space key must be used (e.g. C:\My Path\SomeDocument or /home/some path/somedocument" the argument must be "enclosed in quotes".

-epilogue "\
Hopefully now you can understand what is going on with the whole arguments/parameters thing; and also how I titled each section :)
"
-command exit
Last edited on Aug 23, 2009 at 5:56pm
Aug 7, 2009 at 4:55pm
Aug 10, 2009 at 10:37pm
Also, I would check to make sure that i+1 != argc, if they forgot to add the argument.
Aug 12, 2009 at 9:14am
Good idea. Thanks :)
Aug 12, 2009 at 2:56pm
The reason it would be three is because argv[0] is the path to your program.


Technically... I think argv[0] is the command used to invoke the program. This may or may not include be the full path depending on how the program was invoked.
Aug 12, 2009 at 8:26pm
Actually that's a good point -- I was playing around earlier and if you type, from command line "movefile.exe -in infile.txt -out outdir" argv[0] is "movefile.exe" not "C:\SomeLocation\movefile.exe".
Aug 19, 2009 at 2:33pm
 
argv[1] == "-in"

That actually works for C string comparison? Don't you need to use std::strcmp or an equivalent function?
Aug 21, 2009 at 12:55am
Obviously not, but it may be better to use it. I don't use C strings often, so I tend to accidentally the string functions.
I always use operator==, and it always works for me. I was thinking of rewriting this for formatting and wording.
Aug 21, 2009 at 1:15am
You could also just go:

string(argv[i]) == "stuff"

But you already knew that. ;)
Aug 23, 2009 at 5:46pm
I didn't know you could do that with strings; I thought it was just with the basic data types (e.g. int(myFloat) strips the decimal and the numbers after it... which sucks because before I knew about it I spent half an hour writing a function to do that... it worked, too...)

Anyway; I rewrote the article,
Sep 6, 2009 at 2:02pm
U can do C++-style function conversions(casts) such as:
string(argv[i]) == "stuff"
if the object or data type you are trying to cast TO(in our case std::string) has a defined constructor that takes the kind of data you are trying to cast FROM as a parameter(in our case char*).

And std::string DOES have a defined constructor that takes char* as a parameter. The reason casting works for all the basic types is because all of them are more or less numeric values.

Basically you could cast anything into anything as long as there's a defined method to do it.
Last edited on Sep 6, 2009 at 11:42pm
Sep 6, 2009 at 6:37pm
Hm, but I didn't know you could cast std::string and the primitive types...
Sep 6, 2009 at 11:37pm
So the process was foreign to you all togheter? It's hard to believe you never did stuff like int(3.6) or char(97). Because that's casting. Or at least that's how i know the process is called.
Sep 7, 2009 at 3:42am
In the original post you said
The GUI's executable invokes the calculator, for example, like this: calc add 5 5, calc.exe returns 10. Then the GUI can display "10" in a text box.

does this have to be done through calling system("program_name") and piping the output or is there some other way. If not how do you pipe it.
Sep 7, 2009 at 11:08am
Sep 7, 2009 at 4:55pm
I don't think so, I think you would use fork() and execl(), then use pipe() (on Linux) or use CreateProcess or ShellExecute[ex] in windows.
Sep 8, 2009 at 2:47am
Thanks
Sep 9, 2009 at 7:33pm
LolFactor I know what casting is, I just never used it between objects and primitives, like this std::string(myChar)
Weirdly in C it's like this: (int)myVariableThatIsntAnInt;
Sep 9, 2009 at 8:18pm
Actually, std::string(...) is not a cast, is it creating a temporary unnamed std::string using the constructor. It just so happens that function style casting int(10.0) looks the same.
Sep 10, 2009 at 4:21pm
oh, ok.
Topic archived. No new replies allowed.