问题
I'm trying to write a C program which creates a UNIX shell. In this shell when a UNIX command is typed, the shell should execute it in the foreground or background (background when & is specified). I'm getting the command to run in the foreground but I can't run it in the background.
Here is my code:
#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);
}
This code is for my homework.
回答1:
Read the manpage for fork()
. The return code of 0 means that you are in the child, non-zero (non-negative) means you are the parent. You should have different logic based on that and use system()
(or better exec*()
in the child branch.
Here's the typical logic you should have:
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
}
回答2:
Here is code derived from the code in the question that emits a prompt, gets the line of input, splits it into tokens, detects that the last token is &
, and detects that the first word is exit
and exits the loop. It prints out what its found, carefully. And you now need to handle the fork, exec, wait etc code.
#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);
}
Note that the code does not prevent you from entering too many tokens on a single line. It eventually detects that you've done so, if it hasn't already crashed, via an assert
. You'll need to make that bullet-proof at some point.
Request for further assistance
I'm very new to the fork and waitpid work. Can you help me here?
You've been given good advice in the other answer.
Add:
#include <sys/wait.h>
Add:
static void run_command(char **argv, int bg_flag);
Add:
/*
** 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));
New function:
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);
}
}
You get to decide how verbose your shell should be, but while you're debugging it, more information is better than less.
Also, the error messages should all go to stderr
; I've left a fair number going to stdout
.
来源:https://stackoverflow.com/questions/18923412/use-strtok-and-execute-unix-command-in-background