C でプログラムによって (つまり、コマンド ラインからのリダイレクトを使用せずに) 'tee' 機能を実装して、stdout が stdout とログ ファイルの両方に出力される方法を探しています。これは、コードと stdout に出力されるすべてのリンクされたライブラリの両方で機能する必要があります。これを行う方法はありますか?
5 に答える
あなたpopen()
はティープログラムをすることができました。
fork()
または、次のような子プロセスを介してパイプすることもできますstdout
(私が書いた実際のライブ プログラムから適応したので、動作します!):
void tee(const char* fname) {
int pipe_fd[2];
check(pipe(pipe_fd));
const pid_t pid = fork();
check(pid);
if(!pid) { // our log child
close(pipe_fd[1]); // Close unused write end
FILE* logFile = fname? fopen(fname,"a"): NULL;
if(fname && !logFile)
fprintf(stderr,"cannot open log file \"%s\": %d (%s)\n",fname,errno,strerror(errno));
char ch;
while(read(pipe_fd[0],&ch,1) > 0) {
//### any timestamp logic or whatever here
putchar(ch);
if(logFile)
fputc(ch,logFile);
if('\n'==ch) {
fflush(stdout);
if(logFile)
fflush(logFile);
}
}
putchar('\n');
close(pipe_fd[0]);
if(logFile)
fclose(logFile);
exit(EXIT_SUCCESS);
} else {
close(pipe_fd[0]); // Close unused read end
// redirect stdout and stderr
dup2(pipe_fd[1],STDOUT_FILENO);
dup2(pipe_fd[1],STDERR_FILENO);
close(pipe_fd[1]);
}
}
「popen()
ティー」の答えは正しかった。まさにそれを行うプログラムの例を次に示します。
#include "stdio.h"
#include "unistd.h"
int main (int argc, const char * argv[])
{
printf("pre-tee\n");
if(dup2(fileno(popen("tee out.txt", "w")), STDOUT_FILENO) < 0) {
fprintf(stderr, "couldn't redirect output\n");
return 1;
}
printf("post-tee\n");
return 0;
}
説明:
popen()
を返しますがFILE*
、dup2()
ファイル記述子 (fd) を想定しているため、 を fd にfileno()
変換しFILE*
ます。次にdup2(..., STDOUT_FILENO)
、 stdout を からの fd に置き換えるように言いpopen()
ます。
つまり、すべての入力を標準出力とファイルにコピーする子プロセス ( popen
) を生成してから、標準出力をそのプロセスに移植します。
pipe(2)
andを使用dup2(2)
して、標準出力を読み取り可能なファイル記述子に接続できます。次に、そのファイル記述子を監視する別のスレッドを作成し、取得したすべてをログ ファイルと元の stdout (dup2
パイプを接続する前に別のファイル記述子に保存されます) に書き込みます。ただし、バックグラウンド スレッドが必要です。
実際、vatine によって提案された popen tee メソッドは、おそらくよりシンプルで安全だと思います (タイムスタンプやエンコーディングなど、ログ ファイルで余分なことをする必要がない限り)。
forkpty()
withを使用しexec()
て、監視対象プログラムをそのパラメーターとともに実行できます。forkpty()
プログラムstdinおよびstdoutにリダイレクトされるファイル記述子を返します。ファイル記述子に書き込まれるものはすべて、プログラムの入力です。プログラムによって書き込まれたものはすべて、ファイル記述子から読み取ることができます。
2番目の部分は、プログラムの出力をループで読み取り、ファイルに書き込み、さらにstdoutに出力することです。
例:
pid = forkpty(&fd, NULL, NULL, NULL);
if (pid<0)
return -1;
if (!pid) /* Child */
{
execl("/bin/ping", "/bin/ping", "-c", "1", "-W", "1", "192.168.3.19", NULL);
}
/* Parent */
waitpid(pid, &status, 0);
return WEXITSTATUS(status);
Cでこれを行う簡単な方法はありません。最も簡単な方法は、コマンドとしてteeを、arumentとして目的のログファイルを指定してpopen(3)を呼び出し、次にdup2(2)を新しく開いたファイル記述子として呼び出すことです。 FILE*をfd1に。
しかし、それはちょっと醜いように見えます、そして私はこれを試したことがないと言わなければなりません。