Writing my own shell in C, how do I run Unix execu

2020-07-10 09:39发布

问题:

In one of my courses we're writing our own shell (basically from scratch).

We've already handled writing a lexer and a parser to divide the input into nice "command" structures that are easier to deal with. Likewise, I have a function read_command() that will read one command in at a time and figure out what type of command it is (pipe/and/or/simple/etc.).

I now am trying to write the execute_command(struct command cmd) function that actually takes the command and runs it. I'm struggling on how to even start actually writing this function.

Let's say I just get a very simple cat foo.txt as the command. My command structure will divide it up neatly so I have a word array with both words in it.

Now I want to run the cat executable with the argument foo.txt. I realize I should use the $PATH variable to try to find the executable and then run it with this argument.

I'm struggling with a few major questions:

  1. How do I actually search for the command cat? Keep in mind this program uses C. What functions can I use to search through directories? How do I use PATH to do this?
  2. When I do find the command cat, how can I run it with foo.txt as a parameter? How can this be done in C?

回答1:

  1. Check out getenv(3) and its relatives. You probably don't actually need to directly do any operations on directories, but if you do, you can use opendir(3), readdir(3), closedir(3), etc.
  2. Use fork(2) and execl(3) or one of its relatives (execve(2) is the base-level API).

The man pages themselves or a simple google search for any of these functions will turn up plenty of examples to help you along.



回答2:

You split the command string up into separate strings (command name, arguments), and you store them in a null terminated array of character pointers. You then fork and pass the command name and the command + arguments to execvp(), unless that's verboten. You will eventually have to worry about pipes and I/O redirection too, but the basic idea remains the same.

char *args[MAXARGS];

args[0] = "cat";
args[1] = "foo.txt";
args[2] = 0;

if ((pid = fork()) == 0)
{
    execvp(args[0], args);
    ...print error...
    exit(1);
    /* execvp() does not return unless there's an error */
}
else if (pid < 0)
    ...fork failed...
else
    ...wait for child to finish - or start other commands, or ...

You might dynamically allocate args in a real shell. It gets one step trickier if you have to supply a custom environment to the executed program. Basically, you'll assemble a copy of the environment, and then after forking, set the global environ to the new value - but if the new environment contains a new value for PATH, that will affect the search for the command by execvp(). So you may end up writing your own variant of execvp() because of that. If so, take a leaf out of the BSD (MacOS X) book; there is a function execvP():

 int execvP(const char *file, const char *search_path, char *const argv[]);

which allows you to specify the search path to use, even though you've fixed the environment. The most fundamental call is execve(); this is used by execvp() behind the scenes (possibly via execv()).



标签: c shell unix path