printf()
の動作はstdout
の場所に依存しているようです。
stdout
がコンソールに送信されると、printf()
はラインバッファリングされ、改行が出力された後にフラッシュされます。stdout
がファイルにリダイレクトされる場合、fflush()
が呼び出されない限り、バッファーはフラッシュされません。stdout
がファイルにリダイレクトされる前にprintf()
が使用された場合、以降の(ファイルへの)書き込みはラインバッファリングされ、改行の後にフラッシュされます。stdout
はいつラインバッファリングされ、いつfflush()
を呼び出す必要がありますか?
void RedirectStdout2File(const char* log_path) {
int fd = open(log_path, O_RDWR|O_APPEND|O_CREAT,S_IRWXU|S_IRWXG|S_IRWXO);
dup2(fd,STDOUT_FILENO);
if (fd != STDOUT_FILENO) close(fd);
}
int main_1(int argc, char* argv[]) {
/* Case 1: stdout is line-buffered when run from console */
printf("No redirect; printed immediately\n");
sleep(10);
}
int main_2a(int argc, char* argv[]) {
/* Case 2a: stdout is not line-buffered when redirected to file */
RedirectStdout2File(argv[0]);
printf("Will not go to file!\n");
RedirectStdout2File("/dev/null");
}
int main_2b(int argc, char* argv[]) {
/* Case 2b: flushing stdout does send output to file */
RedirectStdout2File(argv[0]);
printf("Will go to file if flushed\n");
fflush(stdout);
RedirectStdout2File("/dev/null");
}
int main_3(int argc, char* argv[]) {
/* Case 3: printf before redirect; printf is line-buffered after */
printf("Before redirect\n");
RedirectStdout2File(argv[0]);
printf("Does go to file!\n");
RedirectStdout2File("/dev/null");
}
バッファリングされた関数とバッファリングされていない関数を誤って組み合わせているIO関数。そのような組み合わせは、特にコードを移植可能にする必要がある場合に非常に注意深く行う必要があります。移植できないコードを記述するのは不適切です...)
確かに、バッファリングされたものとバッファリングされていないものを組み合わせないようにするのが最善ですIO同じファイル記述子上。
バッファIO:fprintf()
、fopen()
、fclose()
、freopen()
...
Unbuffered IO:write()
、open()
、close()
、dup()
...
dup2()
を使用してstdoutをリダイレクトする場合。関数は、fprintf()
によって満たされたバッファを認識していません。したがって、dup2()
が古い記述子1を閉じると、バッファはフラッシュされず、コンテンツが別の出力にフラッシュされる可能性があります。あなたのケース2aでは、それは_/dev/null
_に送信されました。
あなたの場合、freopen()
の代わりにdup2()
を使用するのが最善です。これはすべての問題を解決します:
FILE
ストリームのバッファーをフラッシュします。 (ケース2a)関数の正しい実装は次のとおりです。
_void RedirectStdout2File(const char* log_path) {
if(freopen(log_path, "a+", stdout) == NULL) err(EXIT_FAILURE, NULL);
}
_
残念ながら、バッファ付きIOは、新しく作成されたファイルの権限を直接設定することはできません。権限を変更するには、他の呼び出しを使用する必要があります。または、移植できないglibc拡張機能を使用できます。 fopen() man page
。
ファイル記述子を閉じないでください。メッセージをファイルにのみ出力する場合は、close(fd)
を削除してstdout_bak_fd
を閉じます。