Написание собственной оболочки Unix на C - Проблемы с PATH и execv

Я пишу свою собственную оболочку на C. Она должна иметь возможность отображать текущий каталог пользователя, выполнять команды на основе полного пути (необходимо использовать execv) и позволять пользователю изменять каталог с компакт-диском.

Это домашнее задание. Преподаватель дал нам только базовый курс по C и очень краткий набросок того, как должна работать программа. Поскольку я не из тех, кто легко сдается, я изучал, как это сделать в течение трех дней, но теперь я в тупике.

Это то, что у меня есть до сих пор:

  • Отображает имя пользователя, имя компьютера и текущий каталог (по умолчанию домашний каталог).
  • Запрашивает у пользователя ввод и получает ввод
  • Разделяет ввод пользователя с помощью " " на массив аргументов
  • Разделяет переменную окружения PATH с помощью «:» на массив токенов.

Я не уверен, как действовать дальше. Я знаю, что мне нужно использовать команду execv, но в своих исследованиях в Google я действительно не нашел примера, который я понимаю. Например, если команда bin/ls, как execv узнает, что нужно отобразить все файлы/папки из домашнего каталога? Как сообщить системе, что я изменил каталог?

Я много пользовался этим сайтом, который оказался полезным: http://linuxgazette.net/111/ramankutty.html, но опять же, я в тупике.

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


person user1287523    schedule 29.09.2012    source источник
comment
Функция chdir() используется для изменения текущего рабочего каталога.   -  person Vaughn Cato    schedule 29.09.2012
comment
Да, в рубрике прямо указано не использовать execvp. @VaughnCato спасибо. Итак, я должен вызывать команду execv как exevc(args[0], args)? Если пользователь вводит bin/ls вместо /bin/ls, нужно ли добавлять его в рабочий каталог?   -  person user1287523    schedule 29.09.2012
comment
Если вы наберете bin/ls, я ожидаю, что bin/ls будет передан в качестве первого аргумента execv. Только в особом случае, когда в команде нет косой черты, вы можете попробовать указать путь.   -  person Vaughn Cato    schedule 29.09.2012
comment
@VaughnCato хорошо, это имеет смысл. Еще пара вопросов. Предполагая, что пользователь просто вводит, например, ls, должен ли я искать в каталогах PATH, чтобы найти путь к программе? Кроме того, я реализовал chdir, но, похоже, он не работает. Как использовать chdir с execv, нужно ли как-то указать новый каталог в аргументах? Спасибо.   -  person user1287523    schedule 29.09.2012
comment
если пользователь вводит ls, вы должны просмотреть каталоги PATH, чтобы найти тот, который включает исполняемый файл с именем «ls». Вы не пытаетесь использовать текущий каталог (если только PATH не содержит . как каталог, что является законным, но не рекомендуется; вам не нужно об этом беспокоиться, потому что файловая система понимает значение каталога .. Так что chdir не имеет значения Но когда вы запускаете программу, она запускается в текущем рабочем каталоге, так что в этом смысле chdir выполняется автоматически.   -  person rici    schedule 29.09.2012
comment
Да, если пользователь вводит ls, а ваш ПУТЬ — /bin:/usr/bin, то вы должны сначала попробовать /bin/ls, а если это не сработает, попробовать /usr/bin/ls.   -  person Vaughn Cato    schedule 29.09.2012
comment
Если пользователь введет cd /home/john, то вы собираетесь выполнить chdir(/home/john), вот и все.   -  person Vaughn Cato    schedule 29.09.2012
comment
@VaughnCato большое спасибо, это так полезно. Я действительно начинаю понимать это сейчас. Итак, три сценария: пользователь вводит ls, ищет ls в каталогах PATH и запускает его. Пользователь вводит /bin/ls, запустите его отдельно. Единственное, что меня беспокоит, это то, что если пользователь вводит bin/ls, вы уверены, что его не следует добавлять в текущий каталог? Если я наберу bin/ls в стандартной консоли, это не сработает, но /bin/ls сработает.   -  person user1287523    schedule 29.09.2012
comment
Вам не нужно проверять, содержит ли каждый каталог исполняемый файл; вы просто создаете имя исполняемого файла, как если бы каталог действительно содержал исполняемый файл, и пытаетесь его выполнить. Если execv() возвращается, вы знаете, что возникла проблема, поэтому вместо этого вы пытаетесь использовать следующую запись в PATH, останавливаясь только тогда, когда не осталось каталогов для попытки. Это вариация на тему «проще попросить прощения, чем разрешения». Помимо всего прочего, выяснить, может ли кто-то выполнить команду, сложно — вдвойне, если в игре есть ACL.   -  person Jonathan Leffler    schedule 29.09.2012
comment
Если пользователь вводит bin/ls, вам не нужно добавлять его к текущему каталогу. Когда вы выполняете execv(bin/ls,args), операционная система автоматически предполагает, что вы говорите о bin/ls относительно текущего каталога, поэтому, если ваш текущий каталог /home/john, она будет искать /home/ john/bin/ls автоматически. Если у вас нет команды ls в /home/john/bin/ls, она завершится ошибкой, как и в обычной оболочке.   -  person Vaughn Cato    schedule 29.09.2012


Ответы (4)


Для реализации команды cd вам просто нужен системный вызов chdir.

#include <unistd.h>

int chdir(
    const char *path /* the path name */
);

Таким образом, вы можете просто вызвать что-то вроде:

int ret1 = chdir("../foo/bar");

Возвращаемое значение chdir равно 0, если можно было перейти в этот каталог, и -1, если произошла ошибка. Для ошибки вы должны объединить справочную страницу.

Текущий каталог может быть проверен любой программой, поэтому, если вы выполняете ls без каких-либо аргументов, то ls проверяет, в каком каталоге он работает, и использует этот каталог в качестве единственного аргумента. Это функция ls, а не вызова execv.

Для второй части.

#include <unistd.h>
int execv(
     const char *path, /* programm path*/
     char *const argv[]/* argument vector*/
);

execv выполняет исполняемый файл с указанным path и с аргументами, указанными в argv. Итак, если вы хотите выполнить /bin/ls ../foo /bar, вам нужно что-то похожее на

char *cmd_str = "/bin/ls";
char *argv[] = {cmd_str, "../foo", "/bar", NULL };
if (execv(cmd_str, argv) == -1 ){
    /* an error occurred */
}

Ошибка, возвращаемая execv, равна -1. Если вы хотите знать, почему он не выполнил команду, проверьте справочные страницы.

NULL в char *argv[] = {cmd_str, "../foo", "/bar", NULL }; указывает на отсутствие других аргументов после NULL.

Третья часть. Система на основе Unix обычно обрабатывает команды с символом / как команды, которые могут быть выполнены напрямую. Это означает, что вы сначала проверяете, есть ли косая черта в данной командной строке.

int ret_value;
if (strchr(cmd_str, '/')
    if (execv(cmd_str, argv) == -1 ){
        /* an error occurred */
    }

Если косой черты нет, то вам нужно пройтись по всем каталогам в PATH и проверить, можете ли вы выполнить команду. Таким образом, данная команда ls ../foo /bar и позволяет предположить, что значение PATH равно ".:/sbin:/bin:/usr/bin". Затем мы попытаемся сначала выполнить ./ls ../foo /bar, затем /usr/bin/ls ../foo /bar и, наконец, /bin/ls ../foo /bar.

Надеюсь это поможет.

person Raphael Ahrens    schedule 29.09.2012

Например, если команда bin/ls, как execv узнает, что нужно отобразить все файлы/папки из домашнего каталога? Как сообщить системе, что я изменил каталог?

У каждого процесса есть текущий рабочий каталог, который можно изменить с помощью chdir. Дочерние процессы наследуют рабочий каталог от своего родителя. Таким образом, в общем случае ваша оболочка будет управлять своим текущим рабочим каталогом в ответ на cd команды, введенные пользователем. При вводе команды, которая не является встроенной, вы fork создадите дочерний процесс, а затем вызовите execv для выполнения двоичного файла.

Если вы хотите учитывать PATH для имен программ, которые не содержат части каталога, вам следует попробовать все возможные комбинации элемента PATH и имени программы. Вы можете либо проверить, существует ли указанный файл, либо просто попытаться запустить его и продолжить со следующего, если это не удастся. Если все вызовы execv завершились неудачно, вам придется вызвать _exit, чтобы завершить дочерний процесс. .

Обратите внимание, что большинство оболочек будут рассматривать любую команду, содержащую /, как путь, который передается непосредственно в execv. Если путь не начинается с /, то это относительный путь, и операционная система разрешает его относительно текущего рабочего каталога. Другими словами, bin/ls из вашего примера будет относиться к двоичному файлу ls в каталоге bin, который является подкаталогом текущего рабочего каталога. Только команды, которые вообще не содержат /, интерпретируются либо как встроенная команда (например, cd), либо как имя некоторого двоичного файла, расположенного в PATH.

Первый аргумент execv — это путь, который вы вычислили. Первый элемент списка argv традиционно равен имени, как оно было введено, т.е. без добавления каталога PATH. После этого первого аргумента передаются любые дополнительные параметры командной строки, за которыми следует NULL для завершения списка.

person MvG    schedule 29.09.2012

Я думаю, проблема в том, что вы считаете, что оболочка отвечает за выполнение работы ls. ls на самом деле не является «частью» оболочки (по крайней мере, в этом случае). Оболочка выполняет программу с именем ls. Большинство комментариев, кажется, объясняют, как найти ls, но я не думаю, что это то, что вас смущает.

Вы должны тщательно обдумать, в чем смысл оболочки, прежде чем писать ее. В комментариях косвенно указывалось на то, что оболочка "просто" должна "вызывать" такие программы, как ls и chdir, а не выполнять их задачи.

person bubchi89    schedule 29.09.2012
comment
ls — это программа, но chdir — библиотечная функция, инкапсулирующая системный вызов. Не смешивайте эти понятия. - person MvG; 29.09.2012
comment
Да, я понял этот факт (ну вроде как. Я не помнил подробностей, просто chdir особенный), когда думал об этом позже, но я думаю, что в основном его путаница связана с назначением оболочки, а не с тем, как писать Это. - person bubchi89; 30.09.2012

ls сам знает, что, если не заданы какие-либо аргументы, предполагается, что файлы в текущем рабочем каталоге будут возвращены getcwd.

person idefixs    schedule 29.09.2012