double-dash command line options instead of getopt - c

How can I accept a command line argument this way:
./a.out --printall
so that inside my program, I have something like
if (printall) {
// do something
}
I don't want to do this:
if (argc == 2)
//PRINTALL exists
since my program can have multiple command line options:
./a.out --printread
./a.out --printwrite
Secondly, I don't want to use getopt , such that the command becomes
./a.out -printall 1
I just find ./a.out --printall cleaner than ./a.out -printall 1
Edit:
I have seen programs that do this:
./a.out --help
I wonder how they work.

(About the argument parsing part of the question:)
You will need getopt_long() from <unistd.h>. This is a GNU extension.
For greater portability, you might consider Boost program options, though that's a compiled library.

Command line arguments cannot be used to trigger conditional compilation. The program has already been compiled before the program is run.
This is a very pedantic answer. For command-line options in general, see Kerrek SB's answer.

Related

How to pass run time arguments to a function in c through a shell script

I have a shell script which has to take arguments from the command line and pass it to a function in C. I tried to search but didn't find understandable solutions. Kindly help me out.
Should the arguments be passed via an option as a command in the shell script?
I have a main function like this:
int main(int argc, char *argv[])
{
if(argc>1)
{
if(!strcmp(argv[1], "ABC"))
{
}
else if(!strcmp(argv[1], "XYZ"))
{
}
}
}
How to pass the parameters ABC/XYZ from the command line through a shell script which in turn uses a makefile to compile the code?
You cannot meaningfully compare strings with == which is a pointer equality test. You could use strcmp as something like argc>1 && !strcmp(argv[1], "XYZ"). The arguments of main have certain properties, see here.
BTW, main's argc is at least 1. So your test argc==0 is never true. Generally argv[0] is the program name.
However, if you use GNU glibc (e.g. on Linux), it provides several ways for parsing program arguments.
There are conventions and habits regarding program arguments, and you'll better follow them. POSIX specifies getopt(3), but on GNU systems, getopt_long is even more handy.
Be also aware that globbing is done by the shell on Unix-like systems. See glob(7).
(On Windows, things are different, and the command line might be parsed by some startup routine à la crt0)
In practice, you'll better use some system functions for parsing program arguments. Some libraries provide a way for that, e.g. GTK has gtk_init_with_args. Otherwise, if you have it, use getopt_long ...
Look also, for inspiration, into the source code of some free software program. You'll find many of them on github or elsewhere.
How to pass the parameters ABC/XYZ from the command line through a shell script
If you compile your C++ program into an executable, e.g. /some/path/to/yourexecutable, you just have to run a command like
/some/path/to/yourexecutable ABC
and if the directory /some/path/to/ containing yourexecutable is in your PATH variable, you can simply run yourexecutable ABC. How to set that PATH variable (which you can query using echo $PATH in your Unix shell) is a different question (you could edit some shell startup file, perhaps your $HOME/.bashrc, with a source code editor such as GNU emacs, vim, gedit, etc...; you could run some export PATH=.... command with an appropriate, colon-separated, sequence of directories).
which in turn uses a makefile to compile the code?
Then you should look into that Makefile and you'll know what is the executable file.
You are using and coding on/for Linux, so you should read something about Linux programming (e.g. ALP or something newer; see also intro(2) & syscalls(2)...) and you need to understand more about operating systems (so read Operating Systems: Three Easy Pieces).
See following simple example:
$ cat foo.c
#include <stdio.h>
int main(int argc, char ** argv)
{
int i;
for (i = 0; i < argc; ++i) {
printf("[%d] %s\n", i, argv[i]);
}
return 0;
}
$ gcc foo.c
$ ./a.out foo bar
[0] ./a.out
[1] foo
[2] bar
$

Parsing double-character command using getopt() C

Is there a way to make getopt() or getopt_long() recognise double character option?
example: ./a.out -my argument where my is single command.
You can use getopt_long_only, which will try to process options as long ones even if there is only one - sign before them.
This function is GNU extension, as well as getopt_long.

Why is libargtable behaving strangely with optional arguemnts?

On the off-chance that anyone uses argtable as a command line argument parser for C-Code, here is my question:
My Intention
I'm programming in C on a Linux platform using the most recent version of the argtable2 library.
What I want to archive is have a program that takes multiple input files and an optional option (let's call it -o). If -o is not provided as an option in a shell call, no output is written by the program whatsoever. If -o is provided by itself the program's output is written to a default file called "output.txt". If the option is provided together with a file name, e.g. -o other.txt, the output should be written to the file name that was given - in this case to "other.txt".
The Problem
In all my tries argtable misbehaved. It interprets the optional value given along with -o as an input file. So ./program -o other.txt inputfile1.dat inputfile2.dat inputfile3.dat would be interpreted as having four inputfiles - the three "inputfile*.dat"s and "other.txt" which is supposed to be the output file.
Reproduce the problem
Here is a shell session to illustrate, what I mean. It uses a minimal example that produces the problem. I'm not sure if I did something wrong or if it is a bug in libargtable2:
confus#confusion:~$ gcc -o argbug argbug.c -largtable2
confus#confusion:~$ ./argbug
Error: missing option INPUT-FILES
Usage:
./argbug [-o [<file>]] INPUT-FILES
-o [<file>] File to write output to. Default is 'output.txt'.
Omit whole option to discard all output
INPUT-FILES Input files
confus#confusion:~$ ./argbug inputfile1.dat inputfile2.dat inputfile3.dat -o other.txt
inputfile[0] = inputfile1.dat # this is okay output
inputfile[1] = inputfile2.dat # as is this line
inputfile[2] = inputfile3.dat # also okay output
inputfile[3] = other.txt # "other.txt" is falsely recognized as an input file
outputfile = output.txt # should be "other.txt" instead of the default "output.txt"
Either way neither I nor Steward, the author of argtable seem to have time to really look into my problem. Any ideas?
I ran your test and found the same problem. Looking into the source, it seems libargtable is handling it correctly, but it boils down to getopt behavior.
If you look beginning at line 647 in getopt.c, you can see that getopt first checks if there is an argument attached without any space in between the option and the argument (e.g. -oother.txt). If that is the case, it handles it. That is the only case in which it will notice an optional argument.
To test this, try
void *argtable[] = { argOutput, end };
in your testcase, and then
./argbug -o other.txt
You will see that it gives an error.
However, it then has an additional piece of code which checks if there is a required option. If so, is will perform an additional search for options to satisfy this even if there is a space between the flag and argument.
Hint for looking at the code: has_arg is an enum with 0=No argument 1=Required arguemnt 2=Optional argument
Short Answer
libargtable will not process optional arguments if there is a space between the flag and the argument. Remove the space and it should work.
I might consider this a bug, but perhaps some people like this behavior.

"Too few arguments" error trying to run my compiled program

I'm trying to code to refresh my memory preparing myself for a course.
int main(){
int x;
for( x = 0;x < 10; x++){
printf("Hello world\n");
}
return 0;
}
But when I tried to run this I get Too few arguments
I compiled the code above using gcc -o repeat file.c Then to run this I just type repeat
Sorry if this was a stupid question, it has been a while since I took the introduction class.
When you type
filename
at a prompt, your OS searches the path. By default, Linux doesn't include the current directory in the path, so you end up running something like /bin/filename, which complains because it wants arguments. To find out what file you actually ran, try
which filename
To run the filename file gcc created in the working directory, use
./filename
Your code compiles fine. Try:
gcc -o helloworld file.c
./helloworld
UPDATE :
Based on more recent comments, the problem is that the executable is named repeat, and you're using csh or tcsh, so repeat is a built-in command.
Type ./repeat rather than repeat.
And when asking questions, don't omit details like that; copy-and-paste your source code, any commands you typed, and any messages you received.
The executable is named file, which is also a command.
To run your own program, type
./file
EDIT :
The above was an educated guess, based on the assumption that:
The actual compilation command was gcc file.c -o file or gcc -o file file.c; and
The predefined file command (man file for information) would produce that error message if you invoke it without arguments.
The question originally said that the compilation command was gcc file.c; now it says gcc -o filename file.c. (And the file command prints a different error message if you run it without arguments).
The correct way to do this is:
gcc file.c -o filename && ./filename
(I'd usually call the executable file to match the name of the source file, but you can do it either way.)
The gcc command, if it succeeds, gives you an executable file in your current directory named filename. The && says to execute the second command only if the first one succeeds (no point in trying to run your program if it didn't compile). ./filename explicitly says to run the filename executable that's in the current directory (.); otherwise it will search your $PATH for it.
If you get an error message Too few arguments, it's not coming from your program; you won't see that message unless something prints it explicitly. The explanation must be that you're running some other program. Perhaps there's already a command on your system called filename.
So try doing this:
gcc file.c -o filename && ./filename
and see what happens; it should run your program. If that works, try typing just
filename
and see what that does. If that doesn't run your program, then type
type -a filename
or
which filename
to see what you're actually executing.
And just to avoid situations like this, cultivate the habit of using ./whatever to execute a program in the current directory.

So the GNU getopt only supports options in the middle?

I'm trying the getopt API:
http://www.gnu.org/s/hello/manual/libc/Example-of-Getopt.html#Example-of-Getopt
But I find it only supports options in the middle?
As I find that it's judging argv[optind] to argv[argc-1] as non-opt arguments.
Is that the case?
GNU getopt allows options anywhere on the command line. It re-orders argv when parsing, though. You can verify this by saving the example code in a file, compiling it, and running the result:
./a.out
./a.out -a
./a.out foo
./a.out -a foo
./a.out foo -a
The last two will give the same results.

Resources