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:
- Standard? C I/O streams (these are used by
printf
,scanf
, ...) - POSIX I/O descriptors (these are integers used by
read
,write
, ...) - 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:
- Allocate a new handle by calling
CreateFile
. - Assign that new handle to the desired std I/O device using
SetStdHandle
. - Associate that new handle with the corresponding C std file descriptor using
_open_osfhandle
(returns a file descriptor number). - 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
Lightweight Memory Leak Debugging on Linux
Opencv Videocapture Lag Due to the Capture Buffer
Is String::C_Str() No Longer Null Terminated in C++11
How to Create a Static Library with G++
Redirect Stdout/Stderr to a String
How to Update an Existing Element of Std::Set
C++ Overload Static Function with Non-Static Function
Do I Need to Explicitly Call the Base Virtual Destructor
Why Include Guards Do Not Prevent Multiple Function Definitions
Printing All Environment Variables in C/C++
Illegal Token on Right Side of ::
Is C/C++ Bool Type Always Guaranteed to Be 0 or 1 When Typecast'Ed to Int
Arrow Operator (->) in Function Heading
Why Can't the Compiler Deduce the Template Type from Default Arguments