system()
を介して外部アプリケーションを起動しようとしています-たとえば、system("ls")
。発生した出力をキャプチャして、さらに処理するために別の関数に送信できるようにします。 C/C++でそれを行う最良の方法は何ですか?
Popenマニュアルから:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
Popen()関数を試してください。 system()などのコマンドを実行しますが、出力を新しいファイルに送信します。ストリームへのポインターが返されます。
FILE *lsofFile_p = popen("lsof", "r");
if (!lsofFile_p)
{
return -1;
}
char buffer[1024];
char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p);
pclose(lsofFile_p);
編集:別の関数ではなく、別のプログラムに出力を渡したいという質問を誤解しています。 popen()はほぼ間違いなくあなたが望むものです。
システムはシェルへのフルアクセスを提供します。引き続き使用したい場合は、system( "ls> tempfile.txt")によって出力を一時ファイルにリダイレクトできますが、安全な一時ファイルを選択するのは大変です。または、別のプログラムを介してリダイレクトすることもできます:system( "ls | otherprogram");
Popen()コマンドを推奨する人もいます。これは、出力を自分で処理できる場合に必要なものです。
FILE *output = popen("ls", "r");
これにより、コマンドの出力を読み取ることができるFILEポインターが得られます。
Pipe()呼び出しを使用してfork()と組み合わせて接続を作成し、新しいプロセスを作成し、dup2()を使用してそれらの標準入出力を変更し、exec()を使用して新しいプログラムを実行し、wait()メインプログラムでそれらを待ちます。これは、シェルと同じようにパイプラインを設定するだけです。詳細と例については、pipe()のマニュアルページを参照してください。
関数popen()
などは、stderrなどをリダイレクトしません。そのために popen3()
と書きました。
これは私のpopen3()のbowdlerisedバージョンです:
int popen3(int fd[3],const char **const cmd) {
int i, e;
int p[3][2];
pid_t pid;
// set all the FDs to invalid
for(i=0; i<3; i++)
p[i][0] = p[i][1] = -1;
// create the pipes
for(int i=0; i<3; i++)
if(pipe(p[i]))
goto error;
// and fork
pid = fork();
if(-1 == pid)
goto error;
// in the parent?
if(pid) {
// parent
fd[STDIN_FILENO] = p[STDIN_FILENO][1];
close(p[STDIN_FILENO][0]);
fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
close(p[STDOUT_FILENO][1]);
fd[STDERR_FILENO] = p[STDERR_FILENO][0];
close(p[STDERR_FILENO][1]);
// success
return 0;
} else {
// child
dup2(p[STDIN_FILENO][0],STDIN_FILENO);
close(p[STDIN_FILENO][1]);
dup2(p[STDOUT_FILENO][1],STDOUT_FILENO);
close(p[STDOUT_FILENO][0]);
dup2(p[STDERR_FILENO][1],STDERR_FILENO);
close(p[STDERR_FILENO][0]);
// here we try and run it
execv(*cmd,const_cast<char*const*>(cmd));
// if we are there, then we failed to launch our program
perror("Could not launch");
fprintf(stderr," \"%s\"\n",*cmd);
_exit(EXIT_FAILURE);
}
// preserve original error
e = errno;
for(i=0; i<3; i++) {
close(p[i][0]);
close(p[i][1]);
}
errno = e;
return -1;
}
最も効率的な方法は、stdout
ストリームをバイパスして、FILE
ファイル記述子を直接使用することです。
_pid_t popen2(const char *command, int * infp, int * outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
if (pipe(p_stdin) == -1)
return -1;
if (pipe(p_stdout) == -1) {
close(p_stdin[0]);
close(p_stdin[1]);
return -1;
}
pid = fork();
if (pid < 0) {
close(p_stdin[0]);
close(p_stdin[1]);
close(p_stdout[0]);
close(p_stdout[1]);
return pid;
} else if (pid == 0) {
close(p_stdin[1]);
dup2(p_stdin[0], 0);
close(p_stdout[0]);
dup2(p_stdout[1], 1);
dup2(::open("/dev/null", O_WRONLY), 2);
/// Close all other descriptors for the safety sake.
for (int i = 3; i < 4096; ++i) {
::close(i);
}
setsid();
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(1);
}
close(p_stdin[0]);
close(p_stdout[1]);
if (infp == NULL) {
close(p_stdin[1]);
} else {
*infp = p_stdin[1];
}
if (outfp == NULL) {
close(p_stdout[0]);
} else {
*outfp = p_stdout[0];
}
return pid;
}
_
childから出力を読み取るには、次のようにpopen2()
を使用します。
_int child_stdout = -1;
pid_t child_pid = popen2("ls", 0, &child_stdout);
if (!child_pid) {
handle_error();
}
char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));
_
書き込みと読み取りの両方:
_int child_stdin = -1;
int child_stdout = -1;
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout);
if (!child_pid) {
handle_error();
}
const char text = "1\n2\n123\n3";
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1);
char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));
_
Windowsでは、system()を使用する代わりに、CreateProcessを使用して、出力をパイプにリダイレクトし、パイプに接続します。
これはPOSIXの方法でも可能だと思いますか?
関数popen()
とpclose()
はあなたが探しているものです。
例として glibcマニュアル をご覧ください。
このページでは: capture_the_output_of_a_child_process_in_c はpopenを使用する場合とfork/exec/dup2/STDOUT_FILENOアプローチを使用する場合の制限について説明しています。
問題が発生しています capentshark出力とpopen .
そして、私はこの制限が私の問題かもしれないと推測しています:
生のファイル記述子ではなく、stdioストリームを返します。これは、出力を非同期的に処理するには不適切です。
他のアプローチで解決策があれば、この答えに戻ります。
実際に、私はチェックしたばかりです:
プロセスが分岐しているため、popenには問題があります。したがって、Shellコマンドが実行されるのを待つ必要がある場合、それを逃す危険があります。私の場合、パイプが機能する前にプログラムが終了しました。
Linuxでtarコマンドを使用してsystem呼び出しを使用することになりました。 systemからの戻り値は、tarの結果でした。
だから:戻り値が必要な場合、それからpopenを使用する必要がないだけでなく、おそらくあなたが望むことをしないでしょう。
2つの異なるプロセスは通常メモリ空間を共有しないため、標準Cで可能かどうかは完全にはわかりません。私が考えられる最も簡単な方法は、2番目のプログラムがその出力をテキストファイル(programname> textfile.txt)にリダイレクトし、そのテキストファイルを読み込んで処理することです。しかし、それは最善の方法ではないかもしれません。