How to redirect the output of system() to a file?
Asked Answered
A

5

7

In this C program

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
    int file = open("Result", O_CREAT|O_WRONLY, S_IRWXU);

    dup2(stdout, file);
    system("ls -l");

    return 0;
}

I'm trying to redirect the output of system() to a file, for that i have used dup2 but it is not working.

What's wrong with this code?
and, please tell me if there is any better way to do this? (without using > at the terminal )

Adda answered 25/6, 2012 at 12:33 Comment(5)
Why don't just use the > redirection in the system command?Boxcar
Use system("ls -l > Result"); or make your own fork()/exec*() combination.Simian
Don't use system. It's always wrong. Run the child process yourself without a shell, either using fork and execvp or posix_spawn.Hesta
@R.. Here, "It's always wrong" means system() is always wrong or use of system() is wrong. pls clarify.Adda
I mean system should be considered deprecated because (1) it's hard to do anything useful to it, and (2) trying to do anything useful with it (e.g. passing arguments) almost certainly creates dangerous (potentially security-critical) bugs related to shell escaping.Hesta
K
7

stdout is a FILE * pointer of the standard output stream. dup2 expects file descriptor, also you've messed up the parameters order. Use

dup2(file, 1);

instead.

On the better-way-to-do-this part. This way is bad because you probably want to restore your standard output after this system call completes. You can do this in a variety of ways. You can dup it somewhere and then dup2 it back (and close the dupped one). I personally don't like writing own cat implementations as suggested in other answers. If the only thing you want is redirecting a single shell command with system to a file in the filesystem, then probably the most direct and simple way is to construct the shell command to do this like

system("ls -l > Result");

But you have to be careful if filename (Result) comes from user input as user can supply something like 'Result; rm -rf /*' as the filename.

Also, on the topic of security, you should consider specifying the full path to ls as suggested in the comments:

system("/bin/ls -l > Result");
Kinglet answered 25/6, 2012 at 12:38 Comment(5)
I edited your post to include an absolute path to ls in your example.Rolf
Reverted edit because the OP runs "ls" and this would change the meaning of the system call. The question is also unrelated to absolute or relative paths. I think it's better to ask @Kinglet if he would like to revise his answer.Charlenacharlene
@Andomar, actually, I accepted the edit, but anyway, both your points are valid, I don't see a difference :) Wug, thanks for suggestion, it will be seen in the comments anyway.Kinglet
@unkulunkulu: don't hesitate to reverse my change if you agree with Wug. You can do so by clicking on the "edited X mins ago" and choosing "revert" for the appropriate revision.Charlenacharlene
Calling system without specifying an absolute path is generally a very bad idea because of the potential for tricksy individuals to break your program by playing with PATH. Not a good mistake for a beginner to get into the habit of. Hence, my edit.Rolf
B
5

The simple thing is to use > indeed:

#include <stdio.h>
int main()
{
    system("ls -l > /some/file");

    return 0;
}

An alternative is using popen(), something along the lines of

   #include <stdio.h>
   #include <stdlib.h>
   main()
   {
           char *cmd = "ls -l";
           char buf[BUFSIZ];
           FILE *ptr, *file;

           file = fopen("/some/file", "w");
           if (!file) abort();
           if ((ptr = popen(cmd, "r")) != NULL) {
                   while (fgets(buf, BUFSIZ, ptr) != NULL)
                           fprintf(file, "%s", buf);
                   pclose(ptr);
           }
           fclose(file);
           return 0;
   }
Basicity answered 25/6, 2012 at 12:40 Comment(0)
B
4

You should use the popen() library function and read chunks of data from the returned FILE * and write them to whatever output file you like.

Reference.

Breaking answered 25/6, 2012 at 12:39 Comment(0)
S
0

use dup instead of dup2. dup creates a alias file descriptor, which value should be always the smallest available file descriptor.

new_fd = dup(file); - In this statement file might be having the value 3 (because stdin is 0, stdout is 1 and stderr is 2). so new_fd will be 4

If you want to redirect stdout into file. Then do as below.

close(stdout);
new_fd = dup(file);

Now dup will return 1 as the alias for the file descriptor, because we closed stdout so opened file descriptors are 0,2,3 and 1 is smallest available file descriptor.

If you are using dup2 means, dup2(file,1); - do like this

Scour answered 25/6, 2012 at 13:0 Comment(1)
Using dup() instead of dup2() is only if you don't care where the duplicate is placed. If you care, use dup2() -- that's its explicit purpose! If instead you simply hope for the best, as this answer does, it will work fine until one day the pattern breaks and you won't have any idea why it fails. Those make for some aggravating late night debug sessions which could have been avoided by simply doing it the right way from the start.Incardinate
A
0

The other answers are valid but, since you asked about a way to redirect the system()output without the use of > operator in terminal, I would recommend using the FILE *freopen(const char *filename, const char *mode, FILE *stream) function (that is part of the stdio.h header). This is what best represents the > operator in my opinion.

Your code could become something like:

# include <stdio.h>
# include <stdlib.h>

int main(void){
   
   char line[1024];
   FILE *handle = freopen("result.txt", "w", stdout);
   if(!handle)  
     exit(EXIT_FAILURE); //file didn't open
   
   sprintf(line, "ls -l");
   system(line);
   return 0;
}

I would also suggest to first put the command into a buffer and then call system(). This is done in order to simplify scenarios where you need to execute the same command with different parameters.

Adcock answered 3/4, 2023 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.