exec() any command in C

JJ Liu picture JJ Liu · Nov 11, 2011 · Viewed 25.1k times · Source

Say in C, I want to call execvp() on any string command. Command can just be:

char command[] = "ls -l";
char command[] = "rm *.txt";
char command[] = "cat makefile";

I want to put this command variable inside execvp(). So the exec() flavored function can just run with any kind of arbitrary command.

How can I do that? Thanks.

NOTE: system() is not allowed.

Answer

paxdiablo picture paxdiablo · Nov 11, 2011

If you have to call execvp(), then you will need to split up those strings into an executable name and an array of arguments (the first being the "name" of the program and the last being a NULL pointer).

That means something like:

char cmd1[] = "ls";  char *args1[] = {"ls", "-l", NULL};
char cmd1[] = "rm";  char *args1[] = {"rm", "*.txt", NULL}; // but see
                                                            // globbing below.
char cmd1[] = "cat"; char *args1[] = {"cat", "makefile", NULL};

This is a non-trivial exercise, especially if you want to allow for quoting, globbing, escaping and so forth.

Quoting means you'll have to be careful with commands like:

rm "file with spaces.txt"

in that you can't simply break on the spaces - you'll have to interpret items in the command much the same as the shell does. Simplistic breaking on spaces would give you a command with three arguments for that string above, rather than the correct one.

By globbing, I mean you'll almost certainly have problems with something like *.txt since it's typically the shell which expands these arguments. Passing that directly to execvp() will result in a single argument of literally *.txt rather than many arguments matching all the text files in your current directory.

Quoting means that you'll have to handle things like:

ls -l "file with spaces and \" quote in it"

which will further complicate your parser.

Don't get me wrong, it can be done, but it's probably a damn sight easier just using system().

If you're still thinking of going the execvp() route, you'll have to:

  • split the string into separate tokens (rather hard, since you have to handle quotes and escapes).
  • glob all the arguments, meaning that those with wildcards in them (and only ones that aren't escaped or protected by virtue of being inside quotes) are expanded into multiple arguments.
  • construct the argument array, with the command at the front and a NULL at the end.
  • call execvp() with the parameters being first element in that array and the address of the array.