パイプライン処理・標準入出力によるデータ処理
パイプライン処理とは?
UNIX上では、パイプライン処理という概念が重要である。これは、データ処理を水の流れのようにとらえ、様々なフィルタを通してデータを加工し、自分の望む処理を行うというものである。データを列ごとに加工することを考えよう。例えば、以下のようなデータinput.datがあったとする。
1.0 0.2 2.0 0.4 3.0 0.6 4.0 0.8 5.0 1.0入力したデータの2列目を2倍するようなコマンドNIBAIがあったとすると(実際には、このようなコマンドは存在しないので、打っても実行できないが)、% cat input.dat | ./NIBAI > output.datのようにすると望みの処理が行える。この処理の意味は、catコマンドでinput.datを出力(正確には標準出力)させ、NIBAIコマンドに渡している。NIBAIは、入力(正確には標準入力)されたデータを2倍し、出力する。その出力はoutput.datに落とされて終了となる。
コマンドとパイプ、リダイレクトを組み合わせれば、複雑な処理を色々なコマンドをつないで行わせた後にファイルへ書き込むという処理が可能になる。これがパイプライン処理と呼ばれるもので、UNIXでは必須の処理方法である。
ここで、「|」(縦棒)はパイプと呼び、あるコマンドの出力を別のプログラムの入力に引き渡す操作である。それに対し、「>」はリダイレクトと呼ばれ、あるコマンドの出力をファイルに書き込む操作である。
この処理のメリットは、コマンド一つ一つの機能はシンプルでも組み合わせることで多様な処理を可能にするということである。
% cat input.dat | COMAMND_A | COMMAND_B | COMMAND_C | ..... | COMMAND_Z > output.dat
標準入出力とは?
前節にて、括弧書きで標準入力・標準出力などと書いたが、これらはともにデータの入力先、出力先の一種である。標準出力は、パイプやリダイレクトによって入出力先を変更できるもので、パイプやリダイレクトを行わない場合には
ターミナル上に表示 される。これに対し、標準エラー出力というものがある。これは、パイプやリダイレクトを行ってもターミナル上にしか出力させないものである。例えば、プログラムの実行中に、出力はさせたいがデータの中身に混じっては困るような出力(プログラムの進行形経過を示すメッセージなど)は標準エラー出力にする。
標準入力は、他のコマンドからの入力の場合はパイプにより指定され、何も指定が無ければキーボードからの入力となる。
先の例で言えば、
% cat input.dat | PROGRAM > output.datとあるとき、となる。
- 「cat input.dat」は、標準出力にinput.datの中身を吐き出している。
- 「|」は、catの標準出力をPROGRAMへ標準入力として渡している。
- 「PROGRAM」は、標準入力からデータを読み込み、標準出力に結果を渡している。
- 「>」は、PROGRAMからの出力をoutput.datに落としている。
標準入出力に関して、よく利用するコマンドは以下のものがある。
% echo 文字列 # 文字列を標準出力へ出力する % cat ファイル名 # ファイルを標準出力へ出力する % grep 文字列 ファイル名 # 文字列がファイルに含まれるか検索し、含まれれば行ごと標準出力ちなみに、lsも出力先は標準出力である。例えば、あるディレクトリにdatを含む拡張子を持つファイルを列挙したいときは次のようにする。% ls | grep .dat
C言語による標準入出力処理
C言語では、以下のようにして入出力の種類を指定できる。fscanf(stdin,"...") # 標準入力 fprintf(stdout,"...") # 標準出力 fprintf(stderr,"...") # 標準エラー出力以下の例は、2列(x, y)あるデータを用いて、標準入力から必要な行だけデータを読み取り、yを2倍して標準出力に結果を出力するプログラムである。標準入出力エラー出力を用いているところを赤字にしてある。ファイル名 : nibai.c
#include <stdio.h> #include <stdlib.h> #define MAX_LINE 1000 int main(int argc, char *argv[]) { int i,line; double x[MAX_LINE],y[MAX_LINE],y2[MAX_LINE]; /*引数のチェック。引数は一個で、読み込む行数。 もし引数が一個でなければ、使用方法を標準エラー出力で表示させて終了させる。 ちなみに、変数argcはコマンドそのものも個数に入るので、引数が一個の場合はargc=2である。*/ if(argc!=2){ fprintf(stderr,"usage:cat input.dat | %s line > output.dat\n",argv[0]); exit(1); } line=atoi(argv[1]); /*標準入力からデータの読み込み*/ for(i=0;i<line;i++){ fscanf(stdin,"%lf",&x[i]); fscanf(stdin,"%lf",&y[i]); } /*yを2倍する*/ for(i=0;i<line;i++){ y2[i]=y[i]*2.0; } /*結果を標準出力へ吐き出す*/ for(i=0;i<line;i++){ fprintf(stdout,"%f %f\n",x[i],y2[i]); } return(1); }このプログラムの実行は、例えば20行読み込ませる場合は% gcc nibai.c -o NIBAI % cat input.dat | ./NIBAI 20 > output.datとなる。もし、入力データが2列以上あるときでも、awkを用いれば、好きな2列を選んで入力できる。
% awk '{print $2,$5}' input.dat | ./NIBAI 20 > output.datこの例では、2列目と5列目をawkにより標準出力させ、NIBAIに渡している。
実は、このような処理が標準入出力でデータを渡す際の大きな利点の一つである。つまり、
データを様々な形に加工して、プログラムに引き渡すことができる のである。UNIXコマンドリファレンスに示したようなUNIX組み込みコマンドを組み合わせれば、自分でプログラムを組まなくても既存のUNIXコマンドを組み合わせてデータを加工し、最低限の処理だけを自分のプログラムで行う、ということも可能である。
gnuplotによるプログラム処理結果のダイレクト表示
プログラムの処理結果をファイルにセーブすることなしにgnuplotでプロットすることも、標準入出力を用いれば可能である。
% gnuplot gnuplot% plot "<cat input.dat | ./NIBAI 20 "この例では、input.datを標準入力でNIBAIプログラムに渡し、NIBAIプログラムからの標準出力結果をそのままgnuplotで表示させている。gnuplotによるデータプロットは、gnuplotのページも参考のこと。