Написание моей собственной оболочки на C, как мне запустить исполняемые файлы Unix? - PullRequest
6 голосов
/ 21 января 2012

На одном из моих курсов мы пишем нашу собственную оболочку (в основном с нуля).

Мы уже обрабатывали написание лексера и парсера, чтобы разделить вводные данные на приятные структуры «команд», с которыми легче иметь дело. Аналогично, у меня есть функция read_command(), которая будет считывать по одной команде за раз и выяснять, какой это тип команды (труба / и / или / простая / и т.д.).

Я сейчас пытаюсь написать функцию execute_command(struct command cmd), которая фактически принимает команду и запускает ее. Я пытаюсь понять, как вообще начать писать эту функцию.

Допустим, я просто получаю очень простую cat foo.txt в качестве команды. Моя структура команд будет аккуратно разделена, поэтому у меня есть массив слов с обоими словами.

Теперь я хочу запустить исполняемый файл cat с аргументом foo.txt. Я понимаю, что должен использовать переменную $PATH, чтобы попытаться найти исполняемый файл, а затем запустить его с этим аргументом.

Я борюсь с несколькими основными вопросами:

  1. Как я на самом деле ищу команду cat? Имейте в виду, что эта программа использует C. Какие функции я могу использовать для поиска по каталогам? Как мне использовать PATH для этого?
  2. Когда я нахожу команду cat, как я могу запустить ее с foo.txt в качестве параметра? Как это можно сделать в C?

Ответы [ 2 ]

11 голосов
/ 21 января 2012
  1. Выезд getenv(3) и его родственников.Возможно, вам на самом деле не нужно напрямую выполнять какие-либо операции с каталогами, но если вы это сделаете, вы можете использовать opendir(3), readdir(3), closedir(3) и т. Д.
  2. Используйте fork(2) и execl(3) или одного из его родственников (execve(2) является базовымAPI уровня.)

Сами справочные страницы или простой поиск в Google для любой из этих функций приведут множество примеров, которые помогут вам в этом.

4 голосов
/ 21 января 2012

Вы разбиваете командную строку на отдельные строки (имя команды, аргументы) и сохраняете их в массиве символьных указателей с нулевым символом в конце. Затем вы разветвляетесь и передаете имя команды и команду + аргументы execvp(), если это не verboten. В конечном итоге вам также придется беспокоиться о каналах и перенаправлении ввода / вывода, но основная идея остается прежней.

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 ...

Вы можете динамически размещать args в реальной оболочке. Он становится на один шаг сложнее, если вам нужно предоставить пользовательскую среду для исполняемой программы. По сути, вы соберете копию среды, а затем после разветвления установите глобальное значение environ на новое значение, но если новое окружение содержит новое значение для PATH, это повлияет на поиск команды с помощью execvp(). Поэтому вы можете написать свой собственный вариант execvp() из-за этого. Если это так, возьмите лист из книги BSD (MacOS X); есть функция execvP():

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

, который позволяет указать путь поиска, который вы хотите использовать, даже если вы исправили среду Самый фундаментальный вызов - execve(); это используется execvp() за сценой (возможно, через execv()).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...