tags:

views:

5392

answers:

6

So I'm in Linux and I want to have a program accept arguments when you execute it from the command line.

For example,

./myprogram 42 -b -s

So then the program would store that number 42 as an int and execute certain parts of code depending on what arguments it gets like -b or -s.

+9  A: 

In C, this is done using arguments passed to your main() function:

int main(int argc, char *argv[])
{
    int i = 0;
    for (i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
    return 0;
}

More information can be found online such as this Arguments to main article.

Greg Hewgill
Sorry - that's not a dreadfully good x-ref. There's a bug in the statement that "The declaration of the argv argument is often a novice programmer's first encounter with pointers to arrays of pointers and can prove intimidating" (argv is an array of pointers, not a pointer to an array of pointers).
Jonathan Leffler
Additionally, the next page shows an ad hoc option parser instead of using the standard getopt() or getopt_long() parsers - which is simply bad advice. No - it is not a good reference.
Jonathan Leffler
In C, a reference to an array is an address, just like a pointer is an address. Thus, argv can be referred to as both "an array" and "a pointer to an array". This is one of the beautiful simplicities of C as well as one of the points of confusions.
Shannon Nelson
+3  A: 

Take a look at the getopt library; it's pretty much the gold standard for this sort of thing.

womble
+10  A: 

You could use getopt.

 #include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>

 int
 main (int argc, char **argv)
 {
   int bflag = 0;
   int sflag = 0;
   int index;
   int c;

   opterr = 0;

   while ((c = getopt (argc, argv, "bs")) != -1)
     switch (c)
       {
       case 'b':
         bflag = 1;
         break;
       case 's':
         sflag = 1;
         break;
       case '?':
         if (isprint (optopt))
           fprintf (stderr, "Unknown option `-%c'.\n", optopt);
         else
           fprintf (stderr,
                    "Unknown option character `\\x%x'.\n",
                    optopt);
         return 1;
       default:
         abort ();
       }

   printf ("bflag = %d, sflag = %d\n", bflag, sflag);

   for (index = optind; index < argc; index++)
     printf ("Non-option argument %s\n", argv[index]);
   return 0;
 }
CMS
I've added code example.
J.F. Sebastian
Shouldn't He/She understand "main arguments passing" first? ;)
OscarRyz
It will work on Linux because the getopt() function is GNU getopt() and you don't normally set POSIXLY_CORRECT in the environment, and GNU getopt() then processes option arguments before 'file' arguments, even when they follow a file argument as in the example. On POSIX platforms, it won't work....
Jonathan Leffler
....because the 42 will stop the -b and -s options from being interpreted as options. Of course, the design of the command line is bad too. I'll note that in the comments to the question.
Jonathan Leffler
@Jonathan: The question does have the tag `linux`. Therefore GNU `getopt()` example is appropriate here.
J.F. Sebastian
@J F Sebastian - yes, it does have a Linux tag; that's why it has an upvote from me. The structure of a command line and arguments ordering should not rely on that quirk; well, I still have to work across other platforms, and GNU getopt is not standard on other platforms (for all it is available).
Jonathan Leffler
...continued...As ever, if you do something consciously, knowing what is happening, it is usually OK. If you are unaware of it, you get nasty surprises sooner or later.
Jonathan Leffler
+4  A: 

Consider using getopt_long(). It allows both short and long options in any combination.

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

/* Flag set by `--verbose'. */
static int verbose_flag;

int
main (int argc, char *argv[])
{
  while (1)
    {
      static struct option long_options[] =
    {
      /* This option set a flag. */
      {"verbose", no_argument,       &verbose_flag, 1},
      /* These options don't set a flag.
         We distinguish them by their indices. */
      {"blip",    no_argument,       0, 'b'},
      {"slip",    no_argument,       0, 's'},
      {0,         0,                 0,  0}
    };
      /* getopt_long stores the option index here. */
      int option_index = 0;

      int c = getopt_long (argc, argv, "bs",
         long_options, &option_index);

      /* Detect the end of the options. */
      if (c == -1)
    break;

      switch (c)
    {
    case 0:
      /* If this option set a flag, do nothing else now. */
      if (long_options[option_index].flag != 0)
        break;
      printf ("option %s", long_options[option_index].name);
      if (optarg)
        printf (" with arg %s", optarg);
      printf ("\n");
      break;
    case 'b':
      puts ("option -b\n");
      break;
    case 's':
      puts ("option -s\n");
      break;
    case '?':
      /* getopt_long already printed an error message. */
      break;

    default:
      abort ();
    }
    }

  if (verbose_flag)
    puts ("verbose flag is set");

  /* Print any remaining command line arguments (not options). */
  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      putchar ('\n');
    }

  return 0;
}

Related: Which command line commands style do you prefer?

J.F. Sebastian
+2  A: 

Instead of getopt(), you may also consider using argp_parse() (an alternative interface to the same library).

From libc manual:

getopt is more standard (the short-option only version of it is a part of the POSIX standard), but using argp_parse is often easier, both for very simple and very complex option structures, because it does more of the dirty work for you.

But I was always happy with the standard getopt.

N.B. GNU getopt with getopt_long is GNU LGPL.

jetxee
"getopt is GNU LGPL": That depends on the getopt. It has been implemented several times. The one in Mac OS X is BSD licensed.
dmckee
And AT not all versions of getopt() are GPL or LGPL.
Jonathan Leffler
I agree with your comments and edit my post. Thank you.
jetxee
+1  A: 

Other have hit this one on the head:

  • the standard arguments to main(int argc, char **argv) give you direct access to the command line (after it has been mangled and tokenized by the shell)
  • there are very standard facility to parse the command line: getopt() and getopt_long()

but as you've seen the code to use them is a bit wordy, and quite idomatic. I generally push it out of view with something like:

typedef
struct options_struct {
   int some_flag;
   int other_flage;
   char *use_file;
} opt_t;
/* Parses the command line and fills the options structure, 
 * returns non-zero on error */
int parse_options(opt_t *opts, int argc, char **argv);

Then first thing in main:

int main(int argc, char **argv){
   opt_t opts;
   if (parse_options(&opts,argc,argv)){
      ...
   } 
   ...
}

Or you could use one of the solutions suggested in http://stackoverflow.com/questions/189972/argument-parsing-helpers-for-c-unix/.

dmckee