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.
Related
On GNU/Linux terminal, when I add --help to commands from GNU packages, I get a help text that is formatted in a very consistent way. These help texts list the options accepted by that command with this format:
option characters, long options ............ [aligned] explanation of the option
For example, this is a part of the help text of the man command:
Usage: man [OPTION...] [SECTION] PAGE...
-C, --config-file=FILE use this user configuration file
-d, --debug emit debugging messages
-D, --default reset all options to their default values
--warnings[=WARNINGS] enable warnings from groff
Main modes of operation:
-f, --whatis equivalent to whatis
-k, --apropos equivalent to apropos
-K, --global-apropos search for text in all pages
-l, --local-file interpret PAGE argument(s) as local filename(s)
-w, --where, --path, --location
print physical location of man page(s)
-W, --where-cat, --location-cat
print physical location of cat file(s)
I was wondering if there was a standard way (possibly used by GNU packages) to print with the same format, without having to deal with tab stops etc. I have found getopt from POSIX API to parse these options, but I can't find a way to print a list of them.
The GNU tools use argp, which is part of the GNU C library. It offers more options than getopt, including help.
See: https://www.gnu.org/software/libc/manual/html_node/Argp.html
This question already has an answer here:
Pass File As Command Line Argument
(1 answer)
Closed 8 years ago.
So, I can make my program run and all, but if I'm given
$ ./a.out -f Text.txt
I'm just not sure how to get the program to make the connection that -f indicates a file. What is the logic for doing this?
The main function has signature int main(int argc, char**argv); so you can use the argc (which is positive) & argv arguments. The argv array is guaranteed to have argc+1 elements. The last is always NULL. The others are non-nil, non-aliased zero-byte terminated strings. Notice that often some shell is globbing the arguments before your program is started by execve(2): see glob(7).
For example, if you type (in a Linux terminal) myprog -i a*.c -o foo.txt and if at the moment you type that the shell has expanded (by globbing) a*.c into a1.c and a2.c (because these are the only files whose name start with a and have a .c suffix in the current directory), your myprog executable main program is called with
argc==6
argv[0] containing "myprog" (so you could test that strcmp(argv[0],"myprog") == 0)
argv[1] containing "-i"
argv[2] containing "a1.c"
argv[3] containing "a2.c"
argv[4] containing "-o"
argv[5] containing "foo.txt"
argv[6] being the NULL pointer
In addition you are guaranteed (by the kernel doing the execve(2)) that all the 6 argv pointers are distinct, non-aliasing, and non-overlapping.
GNU libc gives you several ways to parse these arguments: getopt & argp. getopt is standardized in POSIX (but GNU gives you also the very useful getopt_long(3))
I strongly suggest you to follow GNU conventions: accept at least --help and --version
The fact that e.g. -f is for some option and not a file name is often conventional (but see use of -- in program arguments). If you happen to really want a file named -f (which is a very bad idea), use ./-f
Several shells have autocompletion. You need to configure them for that (and you might configure them even for your own programs).
I'm developing a script that checks the version of some installed C programs. The version check is performed with the --version option. However, this option may not be implemented in all the checked programs. When the option is implemented I use:
version=$(./$program_name --version)
But when it's not, the program just starts executing.
If I just execute the program in the background and the stop it if continues running, I can never get the version number. Is there a way to check whether the option is implemented without letting the program run?
Not completely waterproof but I nice start is using strings:
strings /usr/bin/git | grep -- --version
If you are referring to any arbitrary C program, no there is currently no way to reliably check if a program supports an option. You can try guessing with a mixture of ./program --help, ./program --usage, ./program -h and ./program --this-option-is-a-lie-or-some-other-bogus-option-to-give-usage-information. However, it all ultimately boils down to guesswork.
On RedHat and derivates using yum and rpm you can ask the rpm package manager:
$ rpm -q --whatprovides /bin/cat
coreutils-8.4-31.el6_5.1.i686
On Debian and derivated using apt-get you can ask the APT package manager:
$ dpkg-query -S /bin/bash
bash: /bin/bash
Back in the day we always included a what string with version info. Actually part of SCCS, if the program contained strings starting with "#(#)", it is displayed by the what command. C code would like like this:
static char prog_id[] = "#(#) my_program version 1.0 - 3/26/2014";
Anyway try doing a what on the program and see what you get. heh.
Using the getopt() function in C, it is possible to do that:
program -a arg_for_a -b arg_for_b -c operand1 operand2
and it works with no problem.
But, how to make it work this way?:
program operand1 operand2 -a arg_for_a -b arg_for_b -c
In this case, every argument, including the -a, -b etc. are considered to be operands.
I'm trying to make just like like gcc or ssh does:
gcc code.c -o executable
ssh user#host -i file.pem
That is, no matter in what position the options and the operands come, they are recognized properly.
How to make options be recognized properly wherever they are, and every word that do not follow an option be recognized an operand?
If you use the GNU C library's implementation of getopt, then it will work like all GNU utilities do, because almost all of them use it. In particular, (quoting from man 3 getopt):
By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.
That's not quite the same as gcc. gcc cares about the relative order of optional and positional arguments. (For example, it makes a difference where -l goes in the command line.) To do that, you'll have to tell GNU getopt to not permute the arguments. Then, every time getopt reports that it's done, optind will have the index of the next positional argument (if any). You can then use that argument, increment optind, and continue using getopt, which will continue at the next argument.
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.