C++: Redirecting Stdout

Redirect stdout to a file

What your code essentially does is that you open a pipe, then fork the process and in the child process (in commented code) close stdout, duplicate the pipe to stdout and execute and ls command, and then (in non-commented code) write 4 bytes to the pipe. In the parent process, you read data from the pipe and wait for the completion of the child process.

Now you want to redirect stdout to a file. You can do that by opening a file using the open() system call and then duplicating that file descriptor to stdout. Something like (I haven't tested this so beware of bugs in the code):

int filefd = open("foo.txt", O_WRONLY|O_CREAT, 0666);
if (!fork()) {
close(1);//Close stdout
dup(filefd);
execlp("ls", "ls", NULL);
} else {
close(filefd);
wait(NULL);
}
return 0;

However, you can also use the freopen as suggested by the other answer.

However, I have several concerns of your code and of my modified code:

  • The pipe() and open() system calls can fail. You should always check for system call failure.

  • The fork() system call can fail. Ditto.

  • dup2() can be used instead of dup(); otherwise the code will fail if stdin is not open as it duplicates to the first available file descriptor.

  • The execlp() system call can fail. Ditto.

  • I think wait() can be interrupted by a signal (EINTR). It's recommended to wrap it around a wrapper that retries the system call if it's aborted by a signal (errno == EINTR).

How to redirect stdout to a file and then restore stdout back?

You need to check the return values of your function calls. For most functions, you should check for error conditions. Doing so might have revealed the problem that if you want open() to create the requested file in the event that it does not initially exist, then you need to add the O_CREAT flag.

But that's not your main problem here -- you are dealing with a buffering issue. The output from the first printf() is buffered in memory, so even though file descriptor 1 refers to your file at the time that printf() is called, the data you write do not immediately get flushed to the destination file. You then restore the original stdout file handle, so when the data are actually flushed, they go to the (restored) original stdout. Solve this by fflush()ing before switching stdout back:

int pfd = open("file", O_WRONLY | O_CREAT, 0777);
int saved = dup(1);

close(1);
dup(pfd);
close(pfd);
printf("This goes into file\n");
fflush(stdout); // <-- THIS

// restore it back
dup2(saved, 1);
close(saved);
printf("this goes to stdout");

Note also that dup2() is cleaner and safer for duping a file descriptor onto a specific file descriptor number. You do that when you restore, but you should also do it for the initial redirection.

Redirecting stdout in win32 does not redirect stdout

Redirecting standard I/O is a bit more involved on Windows due to the lack of a proper interface to map between arbitrary Win32 file handles and higher layer file descriptors/streams.

There are actually three different layers of I/O on Windows:

  1. Standard? C I/O streams (these are used by printf, scanf, ...)
  2. POSIX I/O descriptors (these are integers used by read, write, ...)
  3. Win32 API I/O handles (used by ReadFile, WriteFile, ...)

Redirecting C Streams

To redirect C streams you can use freopen. For instance, you can redirect C stdout using:

freopen("log.txt", "w", stdout);

This redirection will generally not redirect I/O done by POSIX or Win32 APIs (they would still read/write the attached Console, if any). In addition, this redirection will not be inherited by child processes. (On POSIX-compliant/non-Windows systems, it is typical that the POSIX API is also the system API and the C API is implemented on top of the POSIX API. In these cases, freopen is sufficient.)

Redirecting POSIX I/O descriptors

To redirect I/O at the POSIX API level, you can use dup2. For example, you can reassign file descriptor STDOUT_FILENO to redirect stdout, something like:

int fd = open("log.txt", O_WRONLY);
dup2(fd, STDOUT_FILENO);
close(fd);

Unfortunately on Windows, even redirection at the POSIX API level does not guarantee redirection neither at the C nor the Win32 API levels. Whether this will work or not depends on the effort put in the implementation of the C library to map between POSIX file descriptors and Win32 file handles (presuming the C runtime library you are using layered its I/O on top of POSIX to begin with). There is also no guarantee that this redirection will be inherited by spawned children.

Redirecting Std I/O On Windows!

To correctly redirect I/O on Windows you have to redirect at the lowest level (i.e., the Win32 API level) and fix the linkage at higher levels as follows:

  1. Allocate a new handle by calling CreateFile.
  2. Assign that new handle to the desired std I/O device using SetStdHandle.
  3. Associate that new handle with the corresponding C std file descriptor using _open_osfhandle (returns a file descriptor number).
  4. Redirect the returned file descriptor using the dup2 technique explained above.

Here is a sample snippet to redirect stdout:

HANDLE new_stdout = CreateFileA("log.txt", ...);
SetStdHandle(STD_OUTPUT_HANDLE, new_stdout);
int fd = _open_osfhandle(new_stdout, O_WRONLY|O_TEXT);
dup2(fd, STDOUT_FILENO);
close(fd);

P.S. If you can do without I/O redirection inside the program, then you can simply use the Console's I/O redirection at the command line using a tiny Batch file:

@echo off
start "my_gui_app" "path/to/my_gui_app.exe" 1> "path/to/log.txt"

How to redirect stderr and stdout to the same file

The way open works, it searches for free entry in file descriptor table. Where entries 0, 1 and 2 are reserved for stdin, stdout and stderr respectively.

stdin  - 0
stdout - 1
stderr - 2

If you want to redirect stdout and stderr to log.out you could simply do the following:

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main()
{
int fd = - 1;
fd = open("log.out", O_RDWR | O_CREAT);

dup2(fd, 1); // redirects stdout to log.out
dup2(fd, 2); // redirects stderr to log.out

/* Print to stderr and to stdout */
fprintf(stdout, "%s", "Hello world\n");
fprintf(stderr, "%s", "Stack overflow!\n");
return 0;
}

If you care about order, a call to fflush should follow fprintf as redirecting stdout to a file will not flush buffer on newlines.

Redirect standard output to file and re-establish the standard output with function

I finally got the correct answer, so here is how to temporarily redirects the standard output to a file name "file", then executes the function f, then re-establishes the original standard output:


void redirect_stdout (void (*f)(void), char *file)
{
int fd = open(file, O_CREAT | O_TRUNC | O_WRONLY, 0644);
int savefd = dup(1);
dup2(fd, 1);
f();
dup2(savefd, 1);
close(fd);
close(savefd);
}

Problem when redirecting standard output with dup and dup2 in c

You (probably) just need to flush the buffers:

while ((c = getchar()) != EOF) {  
putchar(c);
}
fflush(stdout); /* Actually write to the file */
dup2(save0, 0);
dup2(save1, 1);

Since you haven't included the full code, it's hard to be certain. Also, make sure you haven't done any read-like functions (eg fread, fgetc, etc) on stdin before this function is called.



Related Topics



Leave a reply



Submit