samtools
の使用をC プログラムに統合しようとしています。このアプリケーションは、 BAMと呼ばれるバイナリ形式でデータを読み取りますstdin
。
$ cat foo.bam | samtools view -h -
...
(これは の無駄な使い方だと思いますが、BAM ファイルのバイトをコマンド ラインcat
でパイプする方法を示しているだけです。これらのバイトは、他のアップストリーム プロセスから取得される可能性があります。)samtools
unsigned char
Cプログラム内で、バイトのチャンクをsamtools
バイナリに書き込み、同時にsamtools
これらのバイトを処理した後に標準出力をキャプチャしたいと思います。
プロセスへの書き込みとプロセスからの読み取りを同時に行うことはできないためpopen()
、公開されている の実装を使用することを検討しました。これはpopen2()
、これをサポートするように作成されているようです。
私は次のテスト コードを書きました。これwrite()
は、同じディレクトリにある BAM ファイルの 4 kB チャンク バイトをsamtools
プロセスにしようとします。次にread()
、出力からのバイトをsamtools
ライン バッファーに格納し、標準エラーに出力します。
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define READ 0
#define WRITE 1
pid_t popen2(const char *command, int *infp, int *outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0)
return -1;
pid = fork();
if (pid < 0)
return pid;
else if (pid == 0)
{
close(p_stdin[WRITE]);
dup2(p_stdin[READ], READ);
close(p_stdout[READ]);
dup2(p_stdout[WRITE], WRITE);
execl("/bin/sh", "sh", "-c", command, NULL);
perror("execl");
exit(1);
}
if (infp == NULL)
close(p_stdin[WRITE]);
else
*infp = p_stdin[WRITE];
if (outfp == NULL)
close(p_stdout[READ]);
else
*outfp = p_stdout[READ];
return pid;
}
int main(int argc, char **argv)
{
int infp, outfp;
/* set up samtools to read from stdin */
if (popen2("samtools view -h -", &infp, &outfp) <= 0) {
printf("Unable to exec samtools\n");
exit(1);
}
const char *fn = "foo.bam";
FILE *fp = NULL;
fp = fopen(fn, "r");
if (!fp)
exit(-1);
unsigned char buf[4096];
char line_buf[65536] = {0};
while(1) {
size_t n_bytes = fread(buf, sizeof(buf[0]), sizeof(buf), fp);
fprintf(stderr, "read\t-> %08zu bytes from fp\n", n_bytes);
write(infp, buf, n_bytes);
fprintf(stderr, "wrote\t-> %08zu bytes to samtools process\n", n_bytes);
read(outfp, line_buf, sizeof(line_buf));
fprintf(stderr, "output\t-> \n%s\n", line_buf);
memset(line_buf, '\0', sizeof(line_buf));
if (feof(fp) || ferror(fp)) {
break;
}
}
return 0;
}
( のローカル コピーについてはfoo.bam
、テストに使用しているバイナリ ファイルへのリンクを次に示します。ただし、テスト目的であれば、どの BAM ファイルでもかまいません。)
コンパイルする:
$ cc -Wall test_bam.c -o test_bam
write()
問題は、呼び出し後にプロシージャがハングすることです。
$ ./test_bam
read -> 00004096 bytes from fp
wrote -> 00004096 bytes to samtools process
[bam_header_read] EOF marker is absent. The input is probably truncated.
Iが呼び出しの直後に変数close()
である場合、ループはハングする前にもう 1 回繰り返されます。infp
write()
...
write(infp, buf, n_bytes);
close(infp); /* <---------- added after the write() call */
fprintf(stderr, "wrote\t-> %08zu bytes to samtools process\n", n_bytes);
...
close()
声明で:
$ ./test_bam
read -> 00004096 bytes from fp
wrote -> 00004096 bytes to samtools process
[bam_header_read] EOF marker is absent. The input is probably truncated.
[main_samview] truncated file.
output ->
@HD VN:1.0 SO:coordinate
@SQ SN:seq1 LN:5000
@SQ SN:seq2 LN:5000
@CO Example of SAM/BAM file format.
read -> 00004096 bytes from fp
wrote -> 00004096 bytes to samtools process
この変更により、コマンドラインで実行した場合に得られるはずの出力が得られますsamtools
が、前述のように、手順が再びハングします。
popen2()
チャンク内のデータを内部バッファに読み書きするためにどのように使用しますか? これが不可能な場合、popen2()
このタスクに適した代替手段はありますか?