我正在尝试在类似unix的xv6操作系统的shell程序中实现基本命令执行。我正在编辑的shell代码部分是runcmd函数,我使用execvp命令执行终端中使用的命令。编译时程序编译没有错误但是当我尝试在命令行上键入命令时没有任何反应。我已经阅读了exec命令的man pages,但我仍然不明白这些参数需要在exec()命令中传递的正确方式,或者何时使用哪个版本的exec()为我还是OS编程的新手。
为了执行命令,我在这里没有实现哪些需要添加?我有下面的runcmd函数的代码:
我刚添加了更多exec语句,其中包含每个命令的二进制路径;但是,只有第一个exec命令有效(在本例中为cd)。当我使用任何其他命令时,命令行执行它就像它是CD一样。如何让它适用于多个命令?
struct cmd {
int type; // ' ' (exec), | (pipe), '<' or '>' for redirection
};
struct
execcmd {
int type; // ' '
char *argv[MAXARGS]; // arguments to the command to be exec-ed
};
// Execute cmd. Never returns.
void
runcmd(struct cmd *cmd)
{
int p[2], r;
struct execcmd *ecmd;
struct pipecmd *pcmd;
struct redircmd *rcmd;
if(cmd == 0)
exit(0);
switch(cmd->type){
default:
fprintf(stderr, "unknown runcmd\n");
exit(-1);
case ' ':
ecmd = (struct execcmd*)cmd;
if(ecmd->argv[0] == 0)
exit(0);
//fprintf(stderr, "exec not implemented\n");
execvp("/bin/cd" , ecmd->argv );
execvp("/bin/grep" , ecmd->argv );
execvp("/bin/echo" , ecmd->argv );
execvp("/bin/cat" , ecmd->argv );
execvp("/bin/ls" , ecmd->argv );
break;
case '>':
case '<':
rcmd = (struct redircmd*)cmd;
//fprintf(stderr, "redir not implemented\n");
execvp("/bin" , ecmd->argv );
runcmd(rcmd->cmd);
break;
case '|':
pcmd = (struct pipecmd*)cmd;
fprintf(stderr, "pipe not implemented\n");
int execl(const char *path, const char *arg, ...);
break;
}
exit(0);
}
答案 0 :(得分:3)
看起来你正试图执行&#34; / bin&#34; 。目录
你应该让exec调用的第一个参数是用户想要运行的二进制文件。
使用perror函数也可以在命令失败时为您提供有用的输出。
答案 1 :(得分:2)
我认为这就是你真正需要的东西:
case ' ':
ecmd = (struct execcmd*)cmd;
if (ecmd->argv[0] == 0)
_exit(0);
execvp(ecmd->argv[0], ecmd->argv);
/* if control reaches this point, execvp failed */
perror(ecmd->argv[0]);
_exit(127);
您可能想知道为什么execvp
将可执行文件作为参数向量中的单独参数加载,当您只是提交它argv[0]
时。这是传统功能;如果我今天从头开始设计这个API,我认为我不会包含它。但是,这个想法是程序的行为可能会有所不同,具体取决于它的argv[0]
。例如,当天ex
和vi
是相同的可执行文件(两个指向相同inode的硬链接 - 尚未发明符号链接),这决定了什么模式根据其argv[0]
启动。而且,自从Unix出现以来,login(1)
已经invoked shells with the first character of argv[0]
set to '-'
;没有/bin/-sh
,所以它实际上需要能够指定与参数向量分开调用的程序。
我现在无法想到 shell 会使用除argv[0]
以外的任何内容作为execvp
的第一个参数的情况。
有关代码的其他更改的说明:
exit
,仅fork
。_exit
打印到strerror(errno)
,不 {{ 1}}。 stderr
是此操作的便捷简写。stdout
调用安全,您必须在perror
之前在父级中调用perror
,立即,并且您的C库必须实现stderr的行缓冲,正确。我之所以提到这一点是因为XV6似乎是一个教学操作系统,你必须自己实现它,我不知道stdio是否是你的责任。