您必须“手动”复制数据以将其发送到两个地方,尽管使用特定于 linux 的 tee 和 splice 系统调用可以稍微提高效率(注意 tee syscall 不是 tee 命令,http://blog.superpat .com/2010/07/08/a-cup-of-tee-and-a-splice-of-cake/)
听起来您可能知道该怎么做,只是希望不这样做,但对于其他人来说,解决方案可能是这样的:
dup2 将原始 stderr(通常是终端)复制到新的文件描述符以保存它。然后用 pipe() 做一个管道。dup2 管道的写端到 fd 2. 关闭原来的写端 fd。现在你的 stderr 是一个管道。启动一个线程或进程。在这个线程中,您将数据从管道的读取端复制到文件和您保存的原始 stderr 中。当线程或进程获得 EOF 读取管道时,将其关闭并退出。
popen("tee") 解决方案是相同的,只是它创建了一个额外的 shell 进程(并且您必须正确引用传递给 shell 的文件名,以防其中有特殊字符......一定要测试奇怪的文件名大于其中的符号、空格和引号...)。如果使用 popen 我认为您可能还有一个(化妆品?)问题,您不能 pclose() 因为您的 stderr 会停止工作,所以您总是会打开一个额外的 fd 并且不会 wait4() 孩子。
如果您使用的是 GLib 之类的东西,它有一个 g_spawn_async 系列函数,可用于在没有 shell 的情况下生成 tee 命令。否则,您将不得不手动执行 fork/exec 操作以避免 shell;您可以执行 tee 命令,或者您可以 fork 但不执行 - 只需在 fork 后让子代码执行 stderr 复制,不要依赖 tee 命令。或者,您可以使用线程而不是进程。
如果使用 fork,您可能会发现 gspawn.c 中的源代码很有帮助;在任何复杂的程序中,例如 FD_CLOEXEC 不是 unix 上的默认值,这都是一场噩梦,并且很容易让孩子继承导致问题的描述符。避免僵尸进程并处理所有错误等等也很烦人。
无论如何,是的,它的代码比人们希望的要多,但是在 coreutils 和 gspawn.c 中的 tee 源代码之间,您可能可以复制大部分代码。