パイプを実行できるシェルを作成する必要があります。たとえば、ls -l | wc -l
"のようなコマンドです。ユーザーが指定したコマンドを次のように解析しました。
"ls" = firstcmd
"-l" = frsarg
"wc" = scmd
"-l" = secarg
コマンドが2つとパイプであるため、2つのフォークを使用する必要があります。コマンドを実行するために書いたコードブロックは次のとおりです。
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
}
else
{
pid=fork();
if(pid==0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
execlp(scmd, scmd, secarg, (char*) NULL);
}
}
したがって、シェルを実行してls -l | wc -l
コマンドを入力すると(たとえば)、execの結果は表示されませんが、シェルは正常に実行され続けます。
奇妙なことに、コマンドの結果は、シェルを「exit」または「^ C」で終了した場合にのみ表示されるということです。
その出力の何が問題になっていますか?コマンドを入力した直後に表示されないのはなぜですか?
親プロセスと子プロセスの両方で、すべてのパイプ記述子を閉じる必要があります(子プロセスでの複製後)。あなたのコードの主な問題は、ライターがまだ存在しているため、親プロセスが書き込み側を閉じていないため、wc
プロセスが終了しないことです。以下に示す変更。また、親プロセスにwaitpid
を追加して、wc
プロセスを待機しました。
pid_t pid;
int fd[2];
pipe(fd);
pid = fork();
if(pid==0)
{
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[READ_END]);
close(fd[WRITE_END]);
execlp(firstcmd, firstcmd, frsarg, (char*) NULL);
fprintf(stderr, "Failed to execute '%s'\n", firstcmd);
exit(1);
}
else
{
pid=fork();
if(pid==0)
{
dup2(fd[READ_END], STDIN_FILENO);
close(fd[WRITE_END]);
close(fd[READ_END]);
execlp(scmd, scmd, secarg,(char*) NULL);
fprintf(stderr, "Failed to execute '%s'\n", scmd);
exit(1);
}
else
{
int status;
close(fd[READ_END]);
close(fd[WRITE_END]);
waitpid(pid, &status, 0);
}
}
うーん、十分に近い。フォーク後、一部のファイル記述子を閉じる処理を怠ります。
ここにいくつかの参照があります:
これが私のコードです:
#include <fcntl.h> //
#include <stdio.h> //
#include <stdlib.h> //
#include <string.h> //
#include <sys/types.h> //
#include <sys/wait.h> //
#include <sys/stat.h> //
#include <termios.h> //
#include <unistd.h> //
//
#define INPUT_END 1 // INPUT_END means where the pipe takes input
#define OUTPUT_END 0 // OUTPUT_END means where the pipe produces output
//
int main(int argc, char* argv[]) //
{ //
pid_t pid1; // [STDIN -> terminal_input, STDOUT -> terminal_output] (of the parent process)
pid_t pid2; //
int fd[2]; //
//
pipe(fd); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input, fd[1] -> pipe_output]
pid1 = fork(); //
//
if(pid1==0) //
{ // I am going to be the wc process (i.e. taking input from the pipe)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
dup2(fd[OUTPUT_END], STDIN_FILENO); // [STDIN -> pipe_output, STDOUT -> terminal_output, fd[1] -> pipe_output] (of the WC process)
close(fd[OUTPUT_END]); // [STDIN -> pipe_output, STDOUT -> terminal_output] (of the WC process)
execlp("wc", "wc", "-l",(char*) NULL); //
} //
else //
{ //
pid2=fork(); //
//
if(pid2==0) //
{ // I am going to be the ls process (i.e. producing output to the pipe)
close(fd[OUTPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the ls process)
dup2(fd[INPUT_END], STDOUT_FILENO); // [STDIN -> terminal_input, STDOUT -> pipe_input, fd[0] -> pipe_input] (of the ls process)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> pipe_input] (of the ls process)
execlp("ls","ls","-l",(char*) NULL); //
} //
//
close(fd[OUTPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output, fd[0] -> pipe_input] (of the parent process)
close(fd[INPUT_END]); // [STDIN -> terminal_input, STDOUT -> terminal_output] (of the parent process)
waitpid(-1, NULL, 0); // As the parent process - we wait for a process to die (-1) means I don't care which one - it could be either ls or wc
waitpid(-1, NULL, 0); // As the parent process - we wait for the another process to die.
// At this point we can safely assume both process are completed
} //
} //
さて、このような単純で効率的な作業は、パイプに関するスクリプトを作成し、Cコードからいくつかのexecコマンドを使用してスクリプトを呼び出すことです。
script.sh
#!/bin/sh
ls -l | wc -l
そして、あなたはちょうどあなたのCプログラムでこのようなことをします
char *argv[] = {"script.sh", NULL};
execv(argv[0], argv);
Cプログラムの同じディレクトリにscript.shを置く必要があることに注意してください。