Shell 似乎识别命令但不执行它们
Shell seems to recognize commands but doesn't execute them
我正在尝试在类 unix xv6 OS 的 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);
}
您似乎正在尝试执行“/bin”目录。
您应该使 exec 调用的第一个参数成为用户想要的二进制文件 运行。
使用 perror 函数也会在命令失败时为您提供有用的输出。
我认为这才是你真正需要的:
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)
has invoked shells with the first character of argv[0]
set to '-'
;没有/bin/-sh
,所以它实际上需要能够从参数向量中单独指定要调用的程序。
我目前想不出 shell 会使用除 argv[0]
以外的任何东西作为 [=11= 的第一个参数的情况].
关于代码其他更改的说明:
- 永远不要在
fork
的子端调用 exit
,只调用 _exit
.
- 当系统调用失败时,总是打印出有问题的文件名和
strerror(errno)
,到stderr
,不是stdout
。 perror
这个操作方便shorthand。
- 为了安全地调用
perror
,您必须在父级中调用 fflush(0)
,在 fork
之前立即,并且您的 C 库必须正确实现 stderr 的行缓冲。我提到这一点是因为 XV6 似乎是一种教学 OS,您必须自己实现它的一些部分,而且我不知道 stdio 是否是您的责任。
我正在尝试在类 unix xv6 OS 的 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);
}
您似乎正在尝试执行“/bin”目录。
您应该使 exec 调用的第一个参数成为用户想要的二进制文件 运行。
使用 perror 函数也会在命令失败时为您提供有用的输出。
我认为这才是你真正需要的:
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)
has invoked shells with the first character of argv[0]
set to '-'
;没有/bin/-sh
,所以它实际上需要能够从参数向量中单独指定要调用的程序。
我目前想不出 shell 会使用除 argv[0]
以外的任何东西作为 [=11= 的第一个参数的情况].
关于代码其他更改的说明:
- 永远不要在
fork
的子端调用exit
,只调用_exit
. - 当系统调用失败时,总是打印出有问题的文件名和
strerror(errno)
,到stderr
,不是stdout
。perror
这个操作方便shorthand。 - 为了安全地调用
perror
,您必须在父级中调用fflush(0)
,在fork
之前立即,并且您的 C 库必须正确实现 stderr 的行缓冲。我提到这一点是因为 XV6 似乎是一种教学 OS,您必须自己实现它的一些部分,而且我不知道 stdio 是否是您的责任。