嗯,我正在尝试使用C为linux编写一个shell。使用函数fork()和execl(),我可以执行每个命令,但现在我无法读取参数:
char * command;
char ** c_args = NULL;
bytes_read = getline (&command, &nbytes, stdin);
command = strtok(command, "\n ");
int arg = 0;
c_arg = strtok(NULL, "\n ");
while( c_arg != NULL ) {
if( c_args == NULL ) {
c_args = (char**) malloc(sizeof(char*));
}
else {
c_args = (char**) realloc( c_args, sizeof(char*) * (arg + 1) );
}
c_args[arg] = (char*) malloc( sizeof(char)*1024 );
strcpy( c_args[arg], c_arg );
c_arg = strtok(NULL, "\n ");
arg++;
}
...
pid_t pid = fork()
...
...
execl( <path>, command, c_args, NULL)
...
...这样,当我尝试传递参数时,我会从命令中得到错误,例如:
ls -l给了我:
ls: cannot access p��: No such file or directory我知道问题出在c_args分配上。它有什么问题?
干杯。
发布于 2012-12-15 15:20:05
不能将execl()用于参数的变量列表;您需要使用execv()或其变体之一(execve()、execvp()等)。只有当您知道编译时出现的所有参数时,才能使用execl()。在大多数情况下,一般的shell不会知道这一点。一个例外情况是,当您执行以下操作时:
execl("/bin/sh", "/bin/sh", "-c", command_line, (char *)0);在这里,您将调用shell来运行单个字符串作为命令行(没有其他参数)。但是,当您处理人们在完整shell中在键盘上键入的内容时,您将无法知道他们在编译时键入了多少参数。
在最简单的情况下,应该使用:
execvp(c_args[0], c_args);第零个参数,即命令名,应该是您传递给execvp()的参数。如果这是一个简单的文件名(没有/),那么它将在您的$PATH环境变量的目录中查找该命令。如果命令名包含斜杠,则它将查找指定的(相对或绝对)文件名,如果存在则执行该文件名,如果不存在则执行该文件名。其他参数都应该在以null结尾的列表c_args中。
现在,可能还有其他内存分配问题;我没有仔细检查代码。不过,您可以通过诊断打印参数列表来检查它们:
char **pargs = c_args;
while (*pargs != 0)
puts(*pargs++);这将在单独的行上打印每个参数。请注意,它不会停止,直到它遇到空指针;关键是要空终止指向参数字符串的指针列表。
下面这段代码:
c_args[arg] = (char*) malloc( sizeof(char)*1024 );
strcpy( c_args[arg], c_arg );在通常情况下看起来有点杀伤力,在极端情况下则是内存分配不足。当你复制字符串的时候,分配足够的长度。我看到您正在使用strtok()拆分字符串-它适用于早期的shell,但是当您处理像ls -l>$tmp这样的命令行时,您会发现strtok()喜欢在您读取分隔符之前将其践踏,这将成为一个主要的负担。但是,在使用它时,您可能不必像这样复制参数;您只需设置c_args[arg++] = result_from_strtok;即可。当确实需要复制时,您可能应该使用strdup();例如,它不会忘记为尾随的'\0'分配足够的空间,并且既不会过度分配,也不会分配不足。
发布于 2012-12-15 16:17:57
Jonathan有一个很好的答案,我只是想补充一些东西。
您可以使用popen或system直接由外壳执行。它们通常是不受欢迎的,因为它们可以很容易地注入,但是如果你正在编写一个开放的shell,我不认为使用它们有什么坏处。
如果您打算使用有限的shell (它接受类似sh的语法),那么可以研究一下wordexp。它做了很多事情,但根据我的经验,它做得太多了,特别是如果你试图编写一个中等安全的解释器(它会做一些愚蠢的事情,比如波浪号扩展和变量替换)。
https://stackoverflow.com/questions/13890222
复制相似问题