作为一个项目,我必须制作自己的shell。我做到了,但我对历史记录功能有一些问题。
这是我的代码:
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;
}
当我执行我的shell并连续输入这些命令时,它们可以工作。的°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。
请帮忙吗?
答案 0 :(得分:1)
当用户命令存储在&#39; saisie&#39;,此命令是重复的,并在参数数组中拆分。我使用&#39; cmd&#39;在执行功能中,使用execvp。
然后有一个大问题,cmd
的固定长度为1,如果你用它来存储execvp
的命令参数,那么你只能存储一件事:{{1} }。
您有两种选择:
使用较大的固定大小,例如NULL
,您可以在其中存储
到99个参数,没有更多。这是最简单的解决方案,但不灵活
足够。虽然有些系统可以限制争论的数量
转到新流程,我不知道所有系统是否有限制,
this和this可能对您有帮助。
根据命令行动态创建char *cmd[100]
指针数组。
这是更多的工作,但这也是更灵活的解决方案。假如说
您的命令行不支持管道(char
)和重定向(|
,
<
,<<
,>
),然后>>
可能如下所示:
split_input
现在您可以像这样使用它:
char **split_input(const char *cmd)
{
if(cmd == NULL)
return NULL;
char **argv = NULL, **tmp;
char *line = strdup(cmd);
if(line == NULL)
return NULL;
const char *delim = " \t\n";
char *token = strtok(line, delim);
if(token == NULL)
{
free(line);
return NULL;
}
size_t len = 0;
do {
char *arg = strdup(token);
if(arg == NULL)
{
free_argv(argv);
free(line);
return NULL;
}
tmp = realloc(argv, (len + 2) * sizeof *argv);
if(tmp == NULL)
{
free_argv(argv);
free(line);
return NULL;
}
argv = tmp;
argv[len++] = arg;
argv[len] = NULL; // argv must be NULL terminated
} while(token = strtok(NULL, delim));
free(line);
return argv;
}
void free_argv(char **argv)
{
if(argv == NULL)
return;
for(size_t i = 0; argv[i]; ++i)
free(argv[i]);
free(argv);
}
你也在做
while( boucle == 1)
{
printf("%s@sam:~ %s> ", nom, (getcwd(cwd, sizeof(cwd))));
fgets(saisie,MAX_INPUT_SZ,stdin);
printf("\n");
char **argv = split_input(saisie);
if(argv == NULL)
{
fprintf(stderr, "Cannot split command line, not enough memory\n");
continue;
}
free(backup[u]);
backup[u] = strdup(argv[0]); // <-- passing argv[0], not argv
// but perhaps what you really want
// is strdup(saisie)
u = (u + 1) % MAXCMD;
b = switchcmd(argv,backup,b,u);
start(argv,b);
b = 0; //débloquage fonction start
free_argv(argv);
}
但问题是backup[u] = strdup(cmd);
是一个cmd
指针数组,char
期望一个
strdup
,你传的错误类型。它应该是const char*
或
strdup(cmd[0])
如果你想存储整个命令。