我正在尝试编写一个创建UNIX shell的C程序。在这个shell中,当键入UNIX命令时,shell应该在前台或后台执行它(当指定&时,后台)。我正在获取在前台运行的命令,但我不能在后台运行它。 这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#define MAX_LENGTH 1024
#define DELIMS " \t\r\n"
int main(int argc, char *argv[])
{
char *cmd, *bg;
char line[MAX_LENGTH];
pid_t fpid,bpid;
int status;
while (1)
{
fpid=10;
bpid=10;
printf("myshell > ");
if (!fgets(line, MAX_LENGTH, stdin))
break;
int j=0;
if(cmd = strtok(line, DELIMS))
{
bg = strtok(line," ");
while(bg!=NULL)
{
printf("%s",bg);
bg = strtok(NULL, " ");
if(strcmp(bg, "&") == 0)
break;
}
printf("%s", bg);
if(strcmp(cmd,"exit")==0)
break;
else if(strcmp(bg,"&")==0)
{
bpid=fork();
//waitpid(bpid,&status,0);
system(line);
exit(0);
}
else
{
//fpid=fork();
//if(fpid==0)
//{
system(line);
// exit(0);
//}
//else
//{
// waitpid(fpid,&status,0);
//}
}
}
}
return(0);
}
有人可以在这个计划中帮助我吗?我的作业真的需要这个。
答案 0 :(得分:1)
阅读fork()
的联机帮助页。返回码0表示您在孩子中,非零(非负)表示您是父母。您应该基于此使用不同的逻辑,并在子分支中使用system()
(或更好的exec*()
。
这是你应该拥有的典型逻辑:
tokenize(line)
if (last token is '&') {
rc = fork();
if (rc < 0)
handle error;
else if (rc > 0) { /* in parent, rc = child pid */
do whatever you planned to do in the parent process
}
else { /* in child */
use exec*() to start the child command
}
}
else { /* foreground execution */
use system() to run command
}
答案 1 :(得分:0)
以下是从问题中的代码派生的代码,该代码发出提示,获取输入行,将其拆分为令牌,检测到最后一个令牌是&
,并检测到第一个单词是{{ 1}}并退出循环。它仔细地打印出它的发现。而你现在需要处理fork,exec,wait等代码。
exit
请注意,代码不会阻止您在一行中输入太多令牌。它最终通过#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_LENGTH 1024
#define DELIMS " \t\r\n"
int main(void)
{
char line[MAX_LENGTH];
char *ps1 = "toysh> ";
while (fputs(ps1, stdout) > 0 && fgets(line, sizeof(line), stdin) != NULL)
{
char *cmd[100];
char *bg = NULL;
int j = 0;
char *tokens = line;
while ((cmd[j++] = strtok(tokens, DELIMS)) != NULL)
tokens = NULL;
assert(j < 100);
/* The line has been tokenized into j-1 tokens */
/* Print the tokens found */
for (int i = 0; i < j; i++)
{
if (cmd[i] != 0)
printf("%d: <<%s>>\n", i, cmd[i]);
else
printf("%d: NULL pointer\n", i);
}
assert(j > 0);
if (j == 1)
continue; // No command
j--;
assert(j > 0);
if (strcmp(cmd[j-1], "&") == 0)
{
printf("== Found &\n");
bg = cmd[j-1];
cmd[--j] = 0;
if (j == 0)
{
puts("Syntax error: cannot have & on its own");
continue;
}
}
if (strcmp(cmd[0], "exit") == 0)
{
printf("== Found exit command\n");
if (bg != NULL)
{
puts("Can't run exit in background");
continue;
}
break;
}
/*
** Now you can do your fork, exec, waitpid work. Note that the
** command is already split into words with the null pointer at
** the end. This is what execv(), execve() and execvp() want
*/
}
putchar('\n');
return(0);
}
检测到你已经这样做了,如果它还没有崩溃的话。你需要在某些时候做出防弹。
我对fork和waitpid工作很新。你能帮帮我吗?
在另一个答案中,你得到了很好的建议。
添加:
assert
添加:
#include <sys/wait.h>
添加:
static void run_command(char **argv, int bg_flag);
新功能:
/*
** Now you can do your fork, exec, waitpid work. Note that the
** command is already split into words with the null pointer at
** the end. This is what execv(), execve() and execvp() want
*/
run_command(cmd, (bg != NULL));
你可以决定你的shell应该是多么冗长,但是当你调试它时,更多的信息要好于更少。
此外,错误消息应全部转到static void run_command(char **argv, int bg_flag)
{
pid_t pid;
fflush(0); // Flush pending output
if ((pid = fork()) < 0)
printf("Fork failed\n");
else if (pid > 0)
{
/* Parent shell */
if (bg_flag == 0)
{
int status;
int corpse;
while ((corpse = waitpid(-1, &status, WNOHANG)) >= 0)
{
if (corpse != 0)
printf("Process %d exited with status 0x%.4X\n",
corpse, status);
if (corpse == 0 || corpse == pid)
break;
}
}
else
printf("%d: %s running in background\n", pid, argv[0]);
}
else
{
/* Child process */
execvp(argv[0], argv);
fprintf(stderr, "%d: failed to execute %s (%d: %s)", (int)getpid(), argv[0], errno, strerror(errno));
exit(1);
}
}
;我给stderr
留下了一个公平的数字。