Can I create my own output stream other than stdout and stderr?

The UNIX man pages for it unistd.h

states:

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 all my header files, I believe it is true.

[/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.  */

      

Even though they are defined, they never seem to be used by any other std file on my machine. I find this strange. Perhaps 0,1 and 2 are used elsewhere in place of certain macros. Macros simply exist to be referenced as an indication of how streams are configured.?

Anyway, we can capture specific output streams in the shell by doing this:

./program 1> stdout.txt
./program 2> stderr.txt
./program > both.txt 2>&1

      

I want to create my own output stream and capture it by doing the following:

./program 3> mine.txt

      

I tried searching for unistd.h

other files included in the list <iostream>

to see how std::cout

and how they work std::cerr

, but as you might imagine, I was lost and confused.

I'm more interested in whether you can do this rather than this is a good idea.

+3


source to share


2 answers


Open file descriptors are inherited by child processes. The operating system takes care of connecting the process to the three standard threads, but you can do any number open()

and then exec()

(preferably after the previous one fork()

). The child can then scan the list of open file descriptors (s /proc/self/fd/

) or somehow "find out" which ones to use.

Here's a small example written in 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;
}

      

The caller of the program is responsible for opening any file descriptors that the program needs to write and signaling this using command line arguments.

To pass it open file descriptor 3, which is attached to the file redir

, we can use the exec

shell utility to open the file descriptors and execute it.

$ exec 3>redir ./a.out 3

      



This closes the current shell after the child exits, so you probably want to try it in a subshell:

$ sh -c 'exec 3>redir ./a.out 3'

      

Or alternatively, don't use exec

but the @ bmargulies redirection syntax mentioned . Here we write to standard error (2) and also to file descriptors 3 and 4, where we redirect 3 to standard output (1) and 4 to a file redir

.

$ ./a.out 2 3 4 3>&1 4>redir
hello, world
hello, world
$ cat redir
hello, world

      

This kind of file descriptor inheritance is heavily used in server processes that allow their (unprivileged) children to have file descriptors to register files, files outside their chroot()

jail, TCP connections, and what not.

Unfortunately, forgetting to close the file descriptor before exec()

executing the child process is a common mistake that can be related to security. There is Valgrind to test this module .

+3


source


When you call open

it returns a number. You pass this number to read

and write

. However, you can run a command like:

mycommand 3 3>bloop.txt

      



And inside mycommand, convert argv [1] to a number and pass it in for writing.

+4


source







All Articles