В качестве проекта я должен сделать свою собственную оболочку. Я сделал это, но у меня есть некоторые проблемы с функцией истории.
Вот кусок моего кода:
int main(int argc, char* argv[])
{
char saisie[300], cwd[1024];
char* nom = getenv("USER");
char* backup[MAXCMD];
int boucle = 1, n = 0, i, u = 0, b = 0;
for(i = 0; i < MAXCMD; i++)
{
backup[i] = NULL;
}
for(i = 0; i < MAX_INPUT_SZ; i++)
{
saisie[i] = 0;
}
char* cmd[MAXPARAMS]; //MAXPARAMS is 20
while( boucle == 1)
{
printf("%s@sam:~ %s> ", nom, (getcwd(cwd, sizeof(cwd))));
fgets(saisie,MAX_INPUT_SZ,stdin);
printf("\n");
split_input(saisie, cmd);
free(backup[u]);
backup[u] = strdup(saisie);
u = (u + 1) % MAXCMD;
b = switchcmd(cmd,backup,b,u);
start(cmd,b);
b = 0; //débloquage fonction start
}
return 0;
}
Я печатаю историю с помощью этой функции:
int historique(char* backup[], int u)
{
int i = u;
int place = 1;
do
{
if (backup[i])
{
printf("%4d: %s\n", place, backup[i]);
place++;
}
i = (i + 1) % MAXCMD;
} while (i != u);
return 0;
}
B используется для блокировки функции выполнения (запуска), когда пользователь вводит "cd" или "history", поскольку это вызовет ошибку.
Вот функция, которая срабатывает, когда пользователь вводит "cd", "history" или "exit":
int switchcmd(char** cmd,char** backup, int b,int u)
{
int i, n = 3, switch_value = 0;
char* error;
char* listcmd[n];
listcmd[0] = "cd";
listcmd[1] = "exit";
listcmd[2] = "history";
for (i = 0; i < n; ++i)
{
if(strcmp(cmd[0], listcmd[i]) == 0)
{
switch_value = i + 1;
break;
}
}
switch (switch_value)
{
case 1:
chdir(cmd[1]);
b = 1;
error = strerror(errno);
if (*error != 0)
{
printf("sam: %s: %s\n", cmd[0], error);
}
break;
case 2:
printf("Bye bye\n");
exit(0);
case 3:
historique((char**)backup,u);
b = 1;
break;
}
return b;
}
Когда я запускаю свою оболочку и последовательно ввожу эти команды, они работают. °i1
> clear
> ls -a -l
> ls -a
> cd ..
> man chdir
Затем "история" для печати истории, у меня есть это: °i2
1: clear
2: ls
3: ls
4: cd
5: man
6: history
и мне нужен этот вывод со всеми параметрами: °i3
1: clear
2: ls -a -l
3: ls -a
4: cd ..
5: man chdir
6: history`
Я не знаю почему, и я не понимаю, почему strdup
не дублирует мой cmd в резервной копии, как это должно быть.
Можете помочь?
Здесь
char* cmd[strlen(saisie)+1];
— первая проблема:saisie
не инициализирован, аstrlen(saisie)
приводит к неопределенному поведению, поэтому длину определить невозможно, и, следовательно, размерcmd
неверен. Из-за этого любой результат, который вы видите, является «мусором».См. определение минимального воспроизводимого примера — каждый вопрос должен охватывать только одну проблему и содержать только кратчайший код, необходимый для ответа на эту единственную конкретную проблему. Привлечение чего-либо еще - например, не ожидание фоновых задач - делает ваш вопрос слишком широким, а включение кода, не связанного с тем, что позволяет другим воспроизводить вашу конкретную проблему с историей, выводит ее за пределы определения MCVE.
@Pablo Я отредактировал программу, но все равно не работает как надо
Вы обновили свой вопрос, но проблема осталась:
char* cmd[strlen(saisie)+1];
приводит к неопределенному поведению, посколькуsaisie
не инициализирован. Все, что следует далее, является "неправильным" из-за неопределенного поведения.Как указывает Пабло, вы не можете использовать strlen() для неинициализированного массива; это просто вернет результаты мусора. Вам придется инициализировать массив чем-то вроде saisie[0] = 0, если вы хотите, чтобы он возвращал 0.
Я в замешательстве, потому что @Pablo вы указываете на то, что [saisie] не инициализирована, и это причина, почему мой [strlen ()] не работает. Я сделал обновление с циклом для инициализации с 0, но [saisie] все еще не инициализирован. Я понимаю, что мне нужно инициализировать [saisie], но после моего обновления я не вижу, кого я могу снова инициализировать [saisie].
@KILIBIBI вы инициализируете
saisie
после того, как вы сделалиstrlen(saisie)
. Вы должны сначала инициализировать, затем вы можете сделатьchar* cmd[strlen(saisie)+1];
. Но то, как вы инициализируетеsaisie
, будет пустой строкой, поэтомуstrlen(saisie)
вернет 0, поэтому вы будете выполнятьchar *cmd[1]
, и размер не может быть изменен позже. Итак, какой смысл иметь массив размерности 1? У вас тоже проблемы с логикой. Какова цельsaisie
?@Pablo 'saisie' используется для хранения команды, введенной пользователем.
А в чем смысл
cmd
? Как я уже сказал, как только вы делаетеchar* cmd[strlen(saisie)+1];
, длина массива фиксируется и больше не может меняться. И в его нынешнем виде (после вашего редактирования) он будет иметь размерность один, поэтому вы можете хранить только указатель 1 наchar
вcmd
. Это не имеет никакого смысла для меня.Когда пользовательская команда хранится в 'saisie', эта команда дублируется и разделяется на массив параметров. И я использую «cmd» в функции выполнения с execvp. @Пабло
Кажется, вы разделили свой ввод на слова , прежде чем сохранить его в массиве истории. Вот почему сохраняется только первое слово. Переместите строку магазина выше
split_input
.@ usr2564301 большое спасибо, теперь у меня есть результат, которого я ожидал!
для удобства чтения и понимания: 1) имена переменных (и имена параметров) должны указывать
usage
илиcontent
Почти все имена в размещенном коде бессмысленны, даже в текущем контексте 2) следуйте аксиоме: только один оператор в строке и (максимум) одно объявление переменной в операторе. 3) постоянно отступайте код, предлагайте, чтобы каждый уровень отступа был 4 пробела. 4) отдельные кодовые блоки (for
if
else
while
do...while
switch
case
<default
) одной пустой строкойпри вызове
strdup()
всегда проверяйте (!=NULL) возвращаемое значение, а когда закончите с этой дублированной строкой, передайте указатель наfree()
, чтобы избежать утечки памяти