Разбор аргументов командной строки для оболочки - PullRequest
0 голосов
/ 02 июля 2019

У меня есть оболочка, которую я пишу для летнего проекта.Я пытаюсь проанализировать командную строку, например,

Если я позвоню

ls -l

, мне нужно проанализировать часть

-l

.

Так что я могу передать его для вектора аргументов, который используется в стороне от execv.Я знаю, что анализирую его правильно, но по какой-то причине что-то не так с поиском каталога.Возможно, я что-то упустил?Ниже мой код.

1 Ответ

5 голосов
/ 02 июля 2019

Хотя стандартная библиотечная функция strtok может быть полезна, вам нужно знать о недостатках ее интерфейса, что по сути является ловушкой для неосторожных.

В этой программе вы, похоже, наткнулись на обе наиболее распространенные проблемы с интерфейсом strtok. Пожалуйста, перечитайте man strtok внимательно в связи с этим ответом, чтобы не попасть в эти проблемы в будущем. Также не используйте strtok в качестве примера хорошего дизайна интерфейса. Вместо этого используйте его как модель для того, чего следует избегать:

Скрытое глобальное состояние

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

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

Это означает, что вы должны быть осторожны, когда у вас есть более одного strtok сканирования в программе. В вашем случае после инициализации переменной с неправильным именем env:

token = strtok(env, ":");

вы используете strtok, чтобы разделить вашу команду ввода на части в переменной с плохим именем argv:

argv = strtok(buf_copy, " ");

поэтому, когда вы позже захотите найти следующий компонент env:

token = strtok(NULL, ":");
Состояние

strtok больше не указывает на env; вместо этого он указывает на buf_copy (и, с вашим конкретным вводом, на точку в buf_copy, где больше не будет найдено токенов).

Модификация входного аргумента

Первый аргумент strtok - это char*, а не const char*.

Как правило, если библиотечная функция имеет строковый аргумент, аргумент должен быть объявлен как const char*, если только функция не намеревается изменить строку. Или, другими словами, объявление const char* - это обещание, что не будет предпринято никаких попыток изменить аргумент, и если это обещание не выполнено, это, вероятно, по уважительной причине.

И действительно, если вы прочитаете документацию strtok, вы увидите, что он явно изменяет свою входную строку, перезаписывая некоторые символы-разделители символом NUL. Это приводит к постоянному разделению исходной строки на отдельные токены. Иногда это нормально, но это может привести к большим неприятностям, если вы захотите снова обратиться к исходному значению строки в будущем. Часто вы будете делать копию оригинальной строки, чтобы вызвать strtok на ней. (Это часто является признаком плохого дизайна программы или сигналом того, что strtok не совсем подходящий инструмент для разбора.)

В этой конкретной программе ловушка заключается в том, что getenv() не возвращает копию значения переменной среды. Он возвращает указатель прямо в таблицу переменных среды. Хотя тип возвращаемого значения getenv равен char*, что может заставить вас поверить, что изменение значения в порядке, стандарт C явно запрещает:

Указанная строка не должна изменяться программой

К сожалению, этот запрет отсутствует на справочной странице Linux для getenv, но на этой справочной странице указано, что getenv дает вам указатель на таблицу окружения. Если вы действительно измените строку, возвращаемую getenv, весьма вероятно (хотя и не гарантировано), что последующий вызов getenv для той же переменной среды вернет измененное значение.

И это именно то, что вы делаете: так как вы позволили strtok потерять строку, возвращаемую getenv(PATH), последующий вызов getenv(PATH) увидит значение, усеченное в первом двоеточии.

...