0

好吧,我正在尝试使用 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 分配。它出什么问题了?

干杯。

4

2 回答 2

2

您不能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环境变量的目录中查找该命令。如果命令名包含斜杠,那么它将查找指定的(相对或绝对)文件名,如果存在则执行,如果不存在则失败。其他参数都应该在以空结尾的列表中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-15T07:20:05.303 回答
1

乔纳森有一个很好的答案,我只是想补充一些东西。

可以使用popenorsystem直接由 shell 执行。它们通常不受欢迎,因为它们可以很容易地注入,但如果你正在编写一个开放的 shell,我看不出使用它们有什么害处。

如果您要使用有限的 shell(它接受类似 sh 的语法),请查看wordexp。它做了很多,但根据我的经验,它做得太多了,特别是如果你正在尝试编写一个适度安全的解释器(它会做一些愚蠢的事情,比如波浪号扩展和变量替换)。

于 2012-12-15T08:17:57.363 回答