5

複数のパイプコマンドを処理するシェルを実装する必要があります。たとえば、これを処理できる必要がありますls | grep -i cs340 | sort | uniq | cut -c 5。問題は、前のコマンドの出力を次のコマンドの入力に渡していないことだと思います。コードを実行すると、出力が表示されません。私はこの擬似コードを使用しています:

for cmd in cmds
    if there is a next cmd
        pipe(new_fds)
    fork
    if child
        if there is a previous cmd
            dup2(old_fds[0], 0)
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            close(new_fds[0])
            dup2(new_fds[1], 1)
            close(new_fds[1])
        exec cmd || die
    else
        if there is a previous cmd
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            old_fds = new_fds
if there are multiple cmds
    close(old_fds[0])
    close(old_fds[1])

複数のパイプを処理する関数のソースコードは次のとおりです。

void execute_multiple_commands(struct command ** commands_to_exec,
        int num_commands_p)
{
    pid_t status;
    int i, err;
    int new_fd[2], old_fd[2];
    pid_t pid, cpid;

    // creating child process
    if ( (cpid = fork()) == -1)
    {
        fprintf(stderr, "Could not create child process, exiting...");
        exit(1);
    }

    if (cpid == 0) // in the child process we run multiple pipe handling
    {
        for (i = 0; i < num_commands_p; i++) // for each cmd in cmds
        {
            if (i+1 < num_commands_p) // if there is next cmd
                pipe(new_fd);

            if ( (pid = fork()) == -1)
            {
                fprintf(stderr, "Could not create child process, exiting...");
                exit(1);
            }

            if (pid == 0) // if child
            {
                if (i != 0) // if there is a previous command
                {
                    dup2(old_fd[0], 0); // setting up old_pipe to input into the child
                    close(old_fd[0]);
                    close(old_fd[1]);

                }
                if (i+1 < num_commands_p) // if there is a next cmd
                {
                    close(new_fd[0]); // setting up new_pipe to get output from child
                    dup2(new_fd[1], 1);
                    close(new_fd[1]);

                    err = execvp(commands_to_exec[i]->args[0], commands_to_exec[i]->args);
                    status = err;
                    exit(err);
                }
            }
            else
            {
                waitpid(pid, &status, 0);
                if (status == -1)
                    exit(1);

                if (i != 0) // if there a previous command
                {
                    close(old_fd[0]);
                    close(old_fd[1]);
                }
                if (i+1 < num_commands_p) // if there a next cmd
                {
                    old_fd[0] = new_fd[0];
                    old_fd[1] = new_fd[1];
                }
                exit(0);
            } // end if
        } // end for

        if (i) // if there a multiple commands
        {
            close(old_fd[0]);
            close(old_fd[1]);
        }
    }
    else // in the parent process we are waiting for child to handle multiple pipes
        waitpid(cpid, &status, 0);
}

関数execvp()は構造体の配列を取ります。解析部分をすべてチェックしましたが、正常に動作します。execute_multiple_commands()困っている機能です。

structのコードは次のとおりです。

// name: command
// desc: holds one command (meaning that it can be
//        more than one token in that command)
//        "ls -la" will be an example of one command
//       holds num of tokens in command array
struct command
{
    char ** args;
    int num_args;
};
4

1 に答える 1

3

新しい戦略 R2 を提案します。

function do(commands)
    if commands is of size 1
        exec commands[0] || die
    split commands into c1 (first command) c2 (the rest of them)
    open
    if fork 
        close input end of pipe
        dup output of pipe to stdin
        do (c2) || die
    close output end of pipe
    dup input of pipe to stdout
    exec c1 || die

特にリストを維持している場合は、再帰関数を使用すると、ロジックを簡素化するのに役立ちます。とにかくアドレス空間全体が上書きされるため、ここではスタックの深さについて心配する必要はありません。

他のニュースでは、マニュアルページから:

これらのシステム コールの 1 つから正常に戻った後、古いファイル記述子と新しいファイル記述子を交換して使用できます。これらは同じオープン ファイルの説明 (open(2) を参照) を参照するため、ファイル オフセットとファイル ステータス フラグを共有します。たとえば、記述子の 1 つで lseek(2) を使用してファイル オフセットを変更すると、もう一方のオフセットも変更されます。

パイプの両端を閉じているとはどういう意味ですか? あなたは本当にそれを閉じています-それと、プログラムが使用しようとしている標準のイン/アウトです。

--> 後で編集 <--

Jonathan Leffler が指摘したように、上記の情報は正しいです。以下のプログラムで確認しました。

#include <unistd.h>

int main(){
    dup2(0, 7);
    write(7, "Hey, 1\n", 7);
    close(0);
    write(7, "Hey, 2\n", 7);
    close(7);
    write(7, "Hey, 3\n", 7);
}

次の出力が得られます。

$ gcc dup2Test.c && ./a.out
Hey, 1
Hey, 2

ありがとう、ジョナサン!

于 2012-10-19T19:44:06.237 に答える