C 言語でパイプからデータを読み取る
 
この記事では、C 言語でパイプから読み取る方法に関する複数の方法を示します。
pipe および read システムコールを使用して、C のパイプから読み取る
    
パイプは、UNIX ベースのシステムにおけるプロセス間通信(IPC)プリミティブの変形の 1つです。これは、単方向通信チャネル、つまり 2つのプロセス間のバイトストリームを提供し、データは一方向に順次移動されます。pipe システムコールは、パイプを作成し、その読み取りおよび書き込み終了用のファイル記述子を取得するために使用されます。通常の I/O 関数読み取りおよび書き込みを使用してパイプ記述子を操作できることに注意してください。pipe システムコールは 2 要素の int 配列を取り、呼び出しが成功すると、最初の読み取りと 2 番目の書き込みの終了を示す 2つのファイル記述子が返されます。パイプに書き込まれたデータは、リーダーが指定されたバイトを取得するまでカーネルにバッファリングされることに注意してください。
次の例では、親プロセスと子プロセスの間で通信するためのパイプの基本的な使用法を示します。最初に、パイプを作成し、その記述子を pipe_fd 配列に格納します。次に、switch ステートメント式で fork を呼び出し、ケース 0 の下に子プロセスのコードブロックを含めます。一方、default の場合は、親プロセスによって実行されます。
親がコマンドライン引数から取得した文字列を書き込み、子が終了するのを待つことに注意してください。その間、子プロセスはパイプから読み取り、読み取ったバイトをコンソールに出力します。子プロセスは fork で親のファイル記述子を継承するため、子と親の両方がパイプの一方の端を閉じます。最後に、送信されたバイトが読み取られた後、パイプの読み取り端が子によって閉じられ、exit 呼び出しで終了します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
enum { BUF_SIZE = 4096 };
int main(int argc, char *argv[]) {
  int pipe_fd[2];
  char buf[BUF_SIZE];
  ssize_t numRead;
  if (argc != 2) {
    fprintf(stderr, "Usage: %s string\n", argv[0]);
    exit(EXIT_FAILURE);
  }
  if (pipe(pipe_fd) == -1) {
    perror("pipe");
    exit(EXIT_FAILURE);
  }
  switch (fork()) {
    case -1:
      perror("fork");
      exit(EXIT_FAILURE);
    case 0:
      if (close(pipe_fd[1]) == -1) {
        perror("close - parent");
        exit(EXIT_FAILURE);
      }
      sleep(3);
      numRead = read(pipe_fd[0], buf, BUF_SIZE);
      write(STDOUT_FILENO, buf, numRead);
      write(STDOUT_FILENO, "\n", 1);
      if (close(pipe_fd[0]) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
      }
      _exit(EXIT_SUCCESS);
    default:
      if (close(pipe_fd[0]) == -1) {
        perror("close - parent");
        exit(EXIT_FAILURE);
      }
      if (write(pipe_fd[1], argv[1], strlen(argv[1])) != strlen(argv[1])) {
        perror("write - partial/failed write");
        exit(EXIT_FAILURE);
      }
      if (close(pipe_fd[1]) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
      }
      wait(NULL);
      exit(EXIT_SUCCESS);
  }
}
while ループを使用して C のパイプから読み取る
前のサンプルコードには、子プロセスで部分的な読み取りが発生する可能性のある 1つの欺瞞的なバグがあります。read 呼び出しはパイプ内よりも少ないデータを返す可能性があるため、この例で 1つの read 呼び出しを呼び出すとエラーになります。幸い、read 関数は読み取りバイト数を返し、次のコードサンプルに示すように、パイプ内のデータを使い果たすループを実装できます。パイプの容量は固定されており、最大値に達すると、リーダーがデータを取得するまで書き込み操作がブロックされることに注意してください。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
enum { BUF_SIZE = 4096 };
int main(int argc, char *argv[]) {
  int pipe_fd[2];
  char buf[BUF_SIZE];
  ssize_t numRead;
  if (argc != 2) {
    fprintf(stderr, "Usage: %s string\n", argv[0]);
    exit(EXIT_FAILURE);
  }
  if (pipe(pipe_fd) == -1) {
    perror("pipe");
    exit(EXIT_FAILURE);
  }
  switch (fork()) {
    case -1:
      perror("fork");
      exit(EXIT_FAILURE);
    case 0:
      if (close(pipe_fd[1]) == -1) {
        perror("close - parent");
        exit(EXIT_FAILURE);
      }
      while (1) {
        numRead = read(pipe_fd[0], buf, BUF_SIZE);
        if (numRead == -1) {
          perror("read");
          exit(EXIT_FAILURE);
        }
        if (numRead == 0) break;
        if (write(STDOUT_FILENO, buf, numRead) != numRead) {
          perror("write - partial/failed write");
          exit(EXIT_FAILURE);
        }
      }
      write(STDOUT_FILENO, "\n", 1);
      if (close(pipe_fd[0]) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
      }
      _exit(EXIT_SUCCESS);
    default:
      if (close(pipe_fd[0]) == -1) {
        perror("close - parent");
        exit(EXIT_FAILURE);
      }
      if (write(pipe_fd[1], argv[1], strlen(argv[1])) != strlen(argv[1])) {
        perror("write - partial/failed write");
        exit(EXIT_FAILURE);
      }
      if (close(pipe_fd[1]) == -1) {
        perror("close");
        exit(EXIT_FAILURE);
      }
      wait(NULL);
      exit(EXIT_SUCCESS);
  }
}
