0

UNIX シェルを作成する C プログラムを作成しようとしています。このシェルでは、UNIX コマンドが入力されると、シェルはそれをフォアグラウンドまたはバックグラウンド (& が指定されている場合はバックグラウンド) で実行する必要があります。フォアグラウンドで実行するコマンドを取得していますが、バックグラウンドで実行できません。

これが私のコードです:

#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);
}

このコードは宿題用です。

4

2 に答える 2

1

これは、プロンプトを発行し、入力行を取得し、それをトークンに分割し、最後のトークンが&であることを検出し、最初の単語が であることを検出して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);
}

このコードは、1 行に多くのトークンを入力することを防止するものではないことに注意してください。まだクラッシュしていない場合は、assert. ある時点で、その防弾を行う必要があります。

さらなる支援の要請

私は fork と waitpid の作業に非常に慣れていません。ここで私を助けてもらえますか?

あなたは他の答えで良いアドバイスを与えられました。

追加:

#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));

新機能:

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;に移動する必要があります。に行くかなりの数を残しましたstdout

于 2013-09-21T18:58:25.003 に答える