Linux 固有の tee および splice システム コール (tee コマンドではなく、tee syscall に注意してください。 .com/2010/07/08/a-cup-of-tee-and-a-splice-of-cake/ )
あなたはそれを行う方法を知っていて、そうしないことを望んでいるように思えますが、他の人にとっては、解決策は次のようなものかもしれません:
元の stderr (通常は端末) を新しいファイル記述子に dup2 して保存します。次に、pipe() でパイプを作成します。dup2 fd 2 へのパイプの書き込み終了。元の書き込み終了 fd を閉じます。これで stderr はパイプになりました。スレッドまたはプロセスを開始します。このスレッドでは、パイプの読み取り側から、保存したファイルと元の stderr の両方にデータをコピーします。スレッドまたはプロセスがパイプを読み取って EOF を取得したら、パイプを閉じて終了します。
popen("tee") ソリューションは、追加のシェル プロセスを作成することを除いて同じです (特殊な文字が含まれている場合に備えて、シェルに渡されるファイル名を適切に引用符で囲む必要があります... で奇妙なファイル名をテストしてください)。大なり記号、スペース、およびそれらの引用符...)。popen を使用している場合、stderr が機能しなくなるため pclose() できないという (表面的な?) 問題もあると思います。
GLib のようなものを使用している場合、g_spawn_async 関数ファミリーがあり、シェルなしで tee コマンドを生成するために使用できます。そうしないと、シェルを回避するために fork/exec を手動で行う必要があります。tee コマンドを実行することも、fork することもできますが、実行することはできません。fork の後の子コードで stderr コピーを実行し、tee コマンドに依存しないでください。または、プロセスの代わりにスレッドを使用できます。
fork を使用している場合は、gspawn.c のソース コードが役立つことがあります。複雑なプログラムでは、たとえば unix で FD_CLOEXEC がデフォルトでないことは非常に悪夢であり、子に問題を引き起こす記述子を継承させるのは簡単です。また、ゾンビ プロセスを回避し、すべてのエラーを処理するのも面倒です。
とにかく、それは期待以上のコードですが、coreutils のティー ソースと gspawn.c の間で、そのほとんどをコピーできる可能性があります。