24

I have the following code:

pid_t pid = fork();
if (pid == -1)
{
    // ...
}
else if (pid == 0)
{
    stdin = someopenfile;
    stdout = someotherfile;
    stderr = somethirdopenfile;
    execvp(args[0], args);
    // handle error ...
}
else
{
    // ...
}

The problem is, the input/output of the execvp() call is still the console, rather than the files. Clearly I am doing something wrong, what is the right way to do this?

Matt
  • 21,026
  • 18
  • 63
  • 115

3 Answers3

49

The right way to do it is to replace the file descriptors STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO with the opened files using dup2(). You should also then close the original files in the child process:

else if (pid == 0)
{
    dup2(fileno(someopenfile), STDIN_FILENO);
    dup2(fileno(someotherfile), STDOUT_FILENO);
    dup2(fileno(somethirdopenfile), STDERR_FILENO);
    fclose(someopenfile);
    fclose(someotheropenfile);
    fclose(somethirdopenfile);
    execvp(args[0], args);
    // handle error ...
}
caf
  • 233,326
  • 40
  • 323
  • 462
  • 5
    Most syscalls can fail. `dup2(2)` is no exception. In particular it can fail with `errno == EMFILE` when reaching the file descriptor limit (which you could lower with `ulimit` bash builtin or `setrlimit(2)` syscall). Read http://www.kernel.org/doc/man-pages/online/pages/man2/dup2.2.html – Basile Starynkevitch Jan 27 '13 at 07:57
  • Don't original `stdin/out/err` need to be closed before doing this? – rustyx Jul 12 '16 at 20:28
  • 2
    @rustyx: No, `dup2()` closes the destination file descriptor first if it's open. – caf Jul 13 '16 at 00:06
  • 1
    How do you revert this? – ytobi Feb 23 '19 at 12:54
  • 1
    @ytobi I'm sure you figured out by now, but the correct answer is using `dup()`, this duplicated descriptor can then be used in the same manner with `dup2()` to replace current file descriptors ones once more. –  Mar 06 '22 at 18:46
12

Take a look at freopen function.

I had to do something similar with stdout and wrote two functions that do the work for me:

static int fd;
static fpos_t pos;

void switchStdout(const char *newStream)
{
  fflush(stdout);
  fgetpos(stdout, &pos);
  fd = dup(fileno(stdout));
  freopen(newStream, "w", stdout);
}

void revertStdout()
{
  fflush(stdout);
  dup2(fd, fileno(stdout));
  close(fd);
  clearerr(stdout);
  fsetpos(stdout, &pos);
}
Jack
  • 131,802
  • 30
  • 241
  • 343
2

You can use this when stdin , stdout , stderr are terminal-

//change stdin,stdout,stderr
    freopen("new_stdin","r",stdin);
    freopen("new_stdout","r",stdout);
    freopen("new_stderr","r",stderr);

    //----do something;

//reset stdin,stdout,stderr
     freopen("/dev/tty","r",stdin);
     freopen("/dev/tty","r",stdout);
     freopen("/dev/tty","r",stderr);
alhelal
  • 916
  • 11
  • 27