スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[C言語Tips]パイプ連結持の注意点

標準入出力を開くことでコマンドをパイプで連結できると言う話を前回したかと思います。
今回はパイプ連結持の注意点について書きたいと思います。

今回のポイント
. パイプで連結されるコマンドは並列に実行される。

以下のコードはパイプで連結すると正しい結果が得られません。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>

const static int CMD_LEN = _MAX_PATH * 2 + 8;

enum RedirectStat
{
RED_NONE = 0,
RED_IN = 1,
RED_OUT = 2
};

int SetRedirectStat(unsigned *stat , const char *input , const char *output)
{
//入力のリダイレクトを明示する。
if (!strcmp(input , "-"))
{
*stat |= RED_IN;
}

//出力のリダイレクトを明示する。
if (!strcmp(output , "-"))
{
*stat |= RED_OUT;
}

return 0;
}

int main (int argc , char **argv)
{
char cmd[CMD_LEN];

if (argc != 3)
{
return -1;
}

char *input = _strdup(argv[1]);
char *output = _strdup(argv[2]);

unsigned stat = RED_NONE;

if (argc != 3)
{
fprintf(stderr , "Usage:Pipe2 input-file output-file");
return -1;
}

//コマンドライン引数からリダイレクトの状態を設定
SetRedirectStat(&stat , input , output);

//標準入出力をバイナリモードに変更
_setmode(_fileno(stdout) , _O_BINARY);
_setmode(_fileno(stdin) , _O_BINARY);

if (stat & RED_OUT)
{
free(output);
output = _strdup("tmp.txt");
}

if (stat & RED_IN)
{
char buf[512];
int size = 0;
FILE *fp;

if (fopen_s(&fp , output , "wb"))
{
fputs("ファイルの作成に失敗しました。\n" , stderr);
}

while (1)
{
size = _read(_fileno(stdin) , buf , sizeof(buf));
_write(_fileno(fp) , buf , size);

if (size < sizeof(buf))
{
break;
}
}
fclose(fp);
}

sprintf_s(cmd , sizeof(cmd) , "copy %s %s" , input , output);
//ファイルのコピー
system(cmd);

if (stat & RED_OUT)
{
//標準出力に指定されたファイルを出力する。
sprintf_s(cmd , sizeof(cmd) , "type %s" , output);
system(cmd);

if (remove(output) != 0)
{
fprintf(stderr , "\n%sの削除に失敗しました。\n" , output);
}
else
{
//今回は分り易くするために一時ファイルの削除を明示する。
fprintf(stderr , "\n%sを削除しました。\n" , output);
}
}

free(input);
free(output);

return 0;
}


出力結果
pipe2_cmd001.jpg

先頭からコマンドが実行されるのであれば問題は起きないはずです。
ですが、パイプは並列で実行されるので、ファイルへのアクセスが重複してしまい、
結果がめちゃくちゃになってしまいます。

対策としてはコマンドごとに一時ファイル名を変えるというのが最も簡単でしょうか。
とりあえずコマンドごとに割り振られたプロセスIDを一時ファイル名にしてみます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <process.h>//getpid();

const static int CMD_LEN = _MAX_PATH * 2 + 8;

enum RedirectStat
{
RED_NONE = 0,
RED_IN = 1,
RED_OUT = 2
};

int SetRedirectStat(unsigned *stat , const char *input , const char *output)
{
//入力のリダイレクトを明示する。
if (!strcmp(input , "-"))
{
*stat |= RED_IN;
}

//出力のリダイレクトを明示する。
if (!strcmp(output , "-"))
{
*stat |= RED_OUT;
}

return 0;
}

int main (int argc , char **argv)
{
char cmd[CMD_LEN];

if (argc != 3)
{
return -1;
}

char *input = _strdup(argv[1]);
char *output = _strdup(argv[2]);

unsigned stat = RED_NONE;

if (argc != 3)
{
fprintf(stderr , "Usage:Pipe2 input-file output-file");
return -1;
}

//コマンドライン引数からリダイレクトの状態を設定
SetRedirectStat(&stat , input , output);

//標準入出力をバイナリモードに変更
_setmode(_fileno(stdout) , _O_BINARY);
_setmode(_fileno(stdin) , _O_BINARY);

if (stat & RED_OUT)
{
char tmp_fname[_MAX_PATH];

sprintf_s(tmp_fname , sizeof(tmp_fname) , "%d" , _getpid());
free(output);
output = _strdup(tmp_fname);
}

if (stat & RED_IN)
{
char buf[512];
int size = 0;
FILE *fp;

if (fopen_s(&fp , output , "wb"))
{
fputs("ファイルの作成に失敗しました。\n" , stderr);
}

while (1)
{
size = _read(_fileno(stdin) , buf , sizeof(buf));
_write(_fileno(fp) , buf , size);

if (size < sizeof(buf))
{
break;
}
}
fclose(fp);
}

sprintf_s(cmd , sizeof(cmd) , "copy %s %s" , input , output);
//ファイルのコピー
system(cmd);

if (stat & RED_OUT)
{
//標準出力に指定されたファイルを出力する。
sprintf_s(cmd , sizeof(cmd) , "type %s" , output);
system(cmd);

if (remove(output) != 0)
{
fprintf(stderr , "\n%sの削除に失敗しました。\n" , output);
}
else
{
//今回は分り易くするために一時ファイルの削除を明示する。
fprintf(stderr , "\n%sを削除しました。\n" , output);
}
}

free(input);
free(output);

return 0;
}


pipe2_cmd002.jpg

今度は正しい結果が得られました。
今回はcopyコマンドを内部で呼び出しただけでしたが、例えば、こちらでオプションを受け取り処理をオプションごとに切り替えてやれば自分でフィルタコマンドを作ることができます。

以下のコードはバイナリに対応した状態で書いてあるので意欲のある方は画像のフィルタリングを連結させてみたりすると面白いと思います。

それではまた次回。

スポンサーサイト

コメントの投稿

非公開コメント

twitter
    follow me on Twitter
    プロフィール

    砂塵

    Author:砂塵
    GIMP2でお絵描きしています。
    主にイラスト練習、プログラミングなどを扱っているブログです。

    Skype始めました。
    SkypeID:sazinn-gimp
    出没時間:平日21~26時、休日(土・日)

    Pixivはじめました
    微エロな絵を載せることがありますので、そういうのが苦手な方や嫌悪感を覚える方はご注意ください。

    カテゴリ
    リンク
    最新記事
    月別アーカイブ
    最新コメント
    RSSリンクの表示
    FC2カウンター
    FC2ブログランキング

    FC2Blog Ranking

    参加ブログカテゴリ
    にほんブログ村 IT技術ブログ プログラム・プログラマーへ
    にほんブログ村 イラストブログ イラスト練習へ
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。