unistd.h
的UNIX手册页指出:
The following symbolic constants are defined for file streams:
STDIN_FILENO File number of stdin. It is 0.
STDOUT_FILENO File number of stdout. It is 1.
STDERR_FILENO File number of stderr. It is 2.
grepping我的所有头文件,我发现这是真的。
[/usr]grep -r "STDIN_FILENO" include
include/unistd.h:#define STDIN_FILENO 0 /* Standard input. */
[/usr] grep -r "STDOUT_FILENO" include
include/unistd.h:#define STDOUT_FILENO 1 /* Standard output. */
[/usr]grep -r "STDERR_FILENO" include
include/boost/asio/detail/impl/handler_tracking.ipp: ::write(STDERR_FILENO, line, length);
include/unistd.h:#define STDERR_FILENO 2 /* Standard error output. */
即使它们被定义,它们似乎永远不会被我的机器上的任何其他std头文件使用。我觉得很奇怪。也许0,1和2在别处使用而不是定义的宏。这些宏只是作为流的配置方式的参考而存在。?
无论如何,我们可以通过这样做捕获shell中的特定输出流:
./program 1> stdout.txt
./program 2> stderr.txt
./program > both.txt 2>&1
我想创建自己的输出流,并通过执行以下操作捕获它:
./program 3> mine.txt
我尝试搜索unistd.h
以及<iostream>
所包含的其他文件来查看std::cout
和std::cerr
的工作方式,但正如您可能想象的那样,我迷失了方向并感到困惑。
我对你能否做到这一点更感兴趣,而不是它是否是一个好主意。
答案 0 :(得分:4)
当您致电open
时,会返回一个号码。您将该号码传递给read
和write
。但是,您可以运行如下命令:
mycommand 3 3>bloop.txt
在mycommand中,将argv [1]转换为数字,并将其传递给write。
答案 1 :(得分:3)
打开的文件描述符由子进程继承。操作系统负责将流程与三个标准流连接,但您可以自由地执行任意数量的open()
,然后执行exec()
(最好是在之前的fork()
之后)。然后,孩子可以扫描打开的文件描述符列表(/proc/self/fd/
)或以某种方式“知道”使用哪些描述符。
这是一个用C语言编写的小例子。
#include <errno.h> /* errno */
#include <stdio.h> /* atoi, fprintf */
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE */
#include <string.h> /* strerror, strlen */
#include <unistd.h> /* write */
static const char *const message = "hello, world\n";
int main(int argc, char * * argv)
{
int fd;
int i;
for (i = 1; i < argc; ++i)
{
fd = atoi(argv[i]);
if (write(fd, message, strlen(message)) < 0)
{
fprintf(stderr, "error: cannot write to fd %d: %s\n",
fd, strerror(errno));
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
程序的调用者负责打开程序应该写入的任何文件描述符,并通过命令行参数告诉它。
要将连接到文件redir
的打开文件描述符3传递给它,我们可以使用shell的exec
实用程序打开文件描述符并执行子文件。
$ exec 3>redir ./a.out 3
这将在子进程退出后关闭当前shell,因此您可能希望在子shell中尝试它:
$ sh -c 'exec 3>redir ./a.out 3'
或者,或者,不要使用exec
,而是提到重定向语法@ bmargulies。在这里,我们写入标准错误输出(2),以及文件描述符3和4,我们将3重定向到标准输出(1),4重定向到文件redir
。
$ ./a.out 2 3 4 3>&1 4>redir
hello, world
hello, world
$ cat redir
hello, world
这种文件描述符的继承在服务器进程中大量使用,这些服务器进程允许其(非特权)子进程使用文件描述符来记录文件,文件描述符以及chroot()
jail之外的文件,TCP连接等等。
不幸的是,在exec()
使用子进程之前忘记关闭文件描述符是一个常见的错误,可能与安全相关。有一个Valgrind模块可以检查这个。