C/C++プログラミング


PC/Linux(Red Hat系)/C++/Java/複雑系/メモ

popen()

外部コマンドをプログラムから使用する方法は、いくつかあります。
あるコマンドを呼んで、その標準出力結果をプログラム上で使用する場合は、
popen()関数をつかうのがもっとも簡単な方法といえます。

使用方法は、使用するコマンドストリングをもとにpopen()コマンドを呼び、
返り値のFILE*を利用してストリングを読み出すだけです。
下の例は、lsコマンドの出力を読み込んで、そのまま出力する例です。

popen.c
#include <stdio.h>
#define COMMAND "ls"

int main(void)
{
 char command[50];
 char field[81];
 FILE *p_file;

 sprintf(command,"%s",COMMAND);

/* ここで、コマンドをpopenでオープン*/
 if(!(p_file=popen(command,"r"))){
   printf(" cannot open a pipe!!");
   exit(1);
 }
 while((fgets(field,80,p_file))!=NULL){
   printf("%s",field);
 }
 pclose(p_file);
 return 0;
}



popen()-2

前回は、popen()を"r"(読みだしモード)で使用することにより、外部コマンドの
標準出力結果をプログラム上で使用しました。 では、その逆で、ある外部コマンドに
値を渡す場合は、どうするのか? これは、popen()を"w"(書き込みモード)で呼び出すことに より実現できます。

下の例は、lsコマンドの出力を読み込んで、内容をgrepに渡し、.cでおわるcプログラム
の名前を表示するものです。

popen2.c
#include <stdio.h>
#define IN_COMMAND "ls"
#define OUT_COMMAND "grep [.]c$"

int main(void)
{
 char command[50];
 char field[81];
 FILE *in_file, *out_file;

 sprintf(command,"%s",IN_COMMAND);

/* ここで、コマンドをpopenで読み込みオープン*/
 if(!(in_file=popen(command,"r"))){
   printf(" cannot open out pipe!!");
   exit(1);
 }
 sprintf(command,"%s",OUT_COMMAND);

/* ここで、コマンドをpopenで書き込みオープン*/
 if(!(out_file=popen(command,"w"))){
   printf(" cannot open in pipe!!");
   exit(1);
 }
 while((fgets(field,80,in_file))!=NULL){
   fputs(field,out_file);
 }
 pclose(in_file);
 pclose(out_file);
 return 0;
}



pipe()

今までの例では、外部コマンドとの双方向のデータのやりとりができません。
これを行うためには、別の手段(pipe)を使って行う必要があります。 pipe()でopenしたパイプをdup2コマンドでstdin/stdoutにつなげなおすことで利用します。

下の例は、pipe()でopenしたパイプを子プロセス内でstdin/stdoutにつなぎかえ、/bin/shを立ち上げています。
/bin/shは、親プロセスによりパイプに書きこまれた物を"stdin"からの入力として扱います。また、"stdout"に
書き込まれた/bin/shからの出力は、親プロセスのパイプから読み出すことができます。

pipe.c
#include <stdio.h>

int main(void)
{
 int filedes_in[2], filedes_out[2]; /* pipe用 file descriptor */
 FILE *fp_to_child, *fp_from_child; /* 親プロセス読み書き用 */
 char  buf[255];

 pipe(filedes_in);
 pipe(filedes_out);

 if(fork()==0){ /* ここから子プロセス*/
   /* この段階で、file_desciptorは、下図のようになる
   /  0(stdin)       --> stdin
   /  1(stdout)      --> stdout
   /  filedes_in[0]  --> pipe1(in)  
   /  filedes_in[1]  --> pipe1(out) 
   /  filedes_out[0] --> pipe2(in)
   /  filedes_out[1] --> pipe2(out)
   */

   close(0); /* stdinをclose */
   dup2(filedes_in[0],0); /* stdin(file descriptor(0))をfildes_in[0]のさすファイルにする。*/
   close(1); /* stdoutをclose */
   dup2(filedes_out[1],1); /* stdout(file descriptor(1))をfildes_out[1]のさすファイルにする。 */

   /* この段階で、file desciptorは、下図のようになる
   /  0(stdin)       --> pipe1(in)
   /  1(stdout)      --> pipe2(out)
   /  filedes_in[0]  --> pipe1(in)  
   /  filedes_in[1]  --> pipe1(out) 
   /  filedes_out[0] --> pipe2(in)
   /  filedes_out[1] --> pipe2(out)
   / この段階で、filedes_xxxは、必要なくなるので、すべてcloseしておく*/
   close(filedes_in[0]);  
   close(filedes_in[1]);  
   close(filedes_out[0]);  
   close(filedes_out[1]);  
     
   execl("/bin/sh","sh",NULL);
   exit();
 }
 /* 親プロセス */ 
 /* 書きこみしやすいように、filedes_xxxをファイルポインタ形式にする。*/
 fp_to_child=fdopen(filedes_in[1],"w");
 fp_from_child=fdopen(filedes_out[0],"r");

 /* 不必要なfiledes_xxxを削除 */
 close(filedes_in[0]);  
 close(filedes_out[1]);  

 /* この段階で、file desciptorは、下図のようになる
 /  0(stdin)       --> stdin
 /  1(stdout)      --> stdout
 /  filedes_in[1]  --> pipe1(out) == FILE * fp_to_child
 /  filedes_out[0] --> pipe2(in)  == FILE * fp_from_child
 */

 printf("コマンドを入れてください:");
 fgets(buf,sizeof(buf),stdin);
 fputs(buf,fp_to_child);
 fflush(fp_to_child);
 fgets(buf,sizeof(buf),fp_from_child);
 printf("コマンドのアウトプット:\n");
 fputs(buf,stdout);
 
 fclose(fp_to_child);
 fclose(fp_from_child);
 wait();
 return 0;
}

トップページに戻る