Skip to the content of the web site.

Command-line Arguments: main( int argc, char *argv[] )

In Unix, when you pass additional arguments to a command, those commands must be passed to the executing process. For example, in calling ls -al, it executes the program ls and passes the string -al as an argument:

% ls
content.html  index.html  primary.0.html
% ls -al
total 20
drwxr-xr-x 2 dwharder users 4096 Sep 11 16:38 .
drwxr-xr-x 6 dwharder users 4096 Sep 11 16:35 ..
-rwxr-xr-x 1 dwharder users  117 Sep 11 16:38 content.html
-rwxr-xr-x 1 dwharder users 1400 Sep 11 16:37 index.html
-rwxr-xr-x 1 dwharder users  532 Sep 11 16:38 primary.0.html
%

The way a running program accesses these additional parameters is that these are passed as parameters to the function main:

int main( int argc, char *argv[] ) {

Here argc means argument count and argument vector.

The first argument is the number of parameters passed plus one to include the name of the program that was executed to get those process running. Thus, argc is always greater than zero and argv[0] is the name of the executable (including the path) that was run to begin this process. For example, if we run

#include <stdio.h>

int main( int argc, char *argv[] ) {
        printf( "argv[0]:  %s\n", argv[0] );

        return 0;
}

Here we compile this code and first compile and run it so that the executable name is a.out and then we compile it again and run it so the executable name is arg:

% gcc argument.0.c
% ./a.out 
argv[0]:  ./a.out
% gcc -o arg argument.0.c
% ./arg 
argv[0]:  ./arg
%

If more additional command-line arguments are passed, the string of all characters is parsed and separated into substrings based on a few rules; however, if all the characters are either characters, numbers or spaces, the shell will separate the based on spaces and assign args[1] the address of the first, args[2] the address of the second, and so on.

The following program prints all the arguments:

#include <stdio.h>

int main( int argc, char *argv[] ) {
        int i;

        printf( "argc:     %d\n", argc );
        printf( "argv[0]:  %s\n", argv[0] );

        if ( argc == 1 ) {
                printf( "No arguments were passed.\n" );
        } else {
                printf( "Arguments:\n" );

                for ( i = 1; i < argc; ++i ) {
                        printf( "  %d. %s\n", i, argv[i] );
                }
        }

        return 0;
}

Here we execute this program with one and then twelve command-line arguments:

% gcc argument.c
% ./a.out first
argc:     2
argv[0]:  ./a.out
Arguments:
  1. first
% ./a.out first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelfth
argc:     13
argv[0]:  ./a.out
Arguments:
  1. first
  2. second
  3. third
  4. fourth
  5. fifth
  6. sixth
  7. seventh
  8. eighth
  9. ninth
  10. tenth
  11. eleventh
  12. twelfth
% 

For Fun

Now, you may be wondering what actually happens (and if you don't, you're welcome to skip this). First, when the command is executed, the shell parses the command line and separates the individual pieces by a null character \0. For example, the execution of

% ./a.out first second third fourth fifth

has the shell generate the string ./a.out☐first☐second☐third☐fourth☐fifth☐ where the box represents the null character. Next, memory for an array of six (argc) pointers is allocated and these six pointers are assigned the first character of each of the strings into which the command line was parsed. Finally, memory is allocated for the stack and using 8-byte alignment, the two arguments are placed into the stack. This is shown in Figure 1.


Figure 1. The result of calling ./a.out first second third fourth fifth on the command line.

Further Fun

In Unix, the shell does further processing of the command. For example, if it finds wild cards that indicate files in the current directory, it will attempt to expand those wild cards. These include ? for one unknown character and * for any number of characters.

For example, the following examines the file in the current directory:

% ls
a.out  argument.0.c  argument.1.c  content.html  images  index.html  primary.0.html  src
% ./a.out *
argc:     9
argv[0]:  ./a.out
Arguments:
  1. a.out
  2. argument.0.c
  3. argument.1.c
  4. content.html
  5. images
  6. index.html
  7. primary.0.html
  8. src
% ./a.out *.html
argc:     4
argv[0]:  ./a.out
Arguments:
  1. content.html
  2. index.html
  3. primary.0.html
% ./a.out argument.?.c
argc:     3
argv[0]:  ./a.out
Arguments:
  1. argument.0.c
  2. argument.1.c

Similarly, you can tell the command line to treat spaces as part of one string or ignore wildcards by either using the backslash before the space or wildcard or surrounding the string by single or double quotes.

% ./a.out hi\ there\?
argc:     2
argv[0]:  ./a.out
Arguments:
  1. hi there?
% ./a.out "hi there?"
argc:     2
argv[0]:  ./a.out
Arguments:
  1. hi there?
% ./a.out 'hi there?'
argc:     2
argv[0]:  ./a.out
Arguments:
  1. hi there?
%

Finally, if you surround text with backticks, it will execute that first and then replace that with the output of the command:

% ./a.out `ls *.c`
argc:     4
argv[0]:  ./a.out
Arguments:
  2. argument.0.c
  3. argument.1.c
%

Guess what happens if you enter ./a.out `ls -al`.