-1

I'd like to know if there is a way to write and output to a file AND have it output on the screen as well without too many lines of code.

I know how to output to console and how to output to file, but it is either one or the other, but I want to bond the two but so that I won't have to repeat the fprintf(stdout,__) once to output to file and once to output to console.

I am working in c.

Thanks in advance.

Edit: While I do also want to know how to do it within command line, I think showing it in code AND command line is important. Just to show two possible solutions to the problem. It should be also independent of any specific OSes... and doesn't look like tee is.

Papbad
  • 163
  • 2
  • 13
  • 2
    easiest way is pipe through `tee` from command line. If you need to do it from within the application check out https://stackoverflow.com/questions/1761950/how-can-i-implement-tee-programmatically-in-c – twain249 Apr 21 '18 at 14:21
  • 1
    Possible duplicate of [How can I implement 'tee' programmatically in C?](https://stackoverflow.com/questions/1761950/how-can-i-implement-tee-programmatically-in-c) – melpomene Apr 21 '18 at 14:26
  • Is it possible to do fprintf(stdout && filename, "__")..? – Papbad Apr 21 '18 at 14:35
  • No, it is not possible. You first have to understand what `&&` means: this is the logical `AND` operator which returns either true or false (in C case not-0 or 0). If neither of the pointer are `NULL`, `stdout && filename` evaluates to 1, so you would be passing `fprintf(1, ...)` which is incorrect. Take a look at the answers. – Pablo Apr 21 '18 at 15:09

2 Answers2

1

You can do it like the following:

#include <stdio.h>
#include <stdarg.h>


void send_string(FILE *fd, char *fmt, ...)
{
    va_list args_fd, args_stdout;

    va_start(args_fd, fmt);
    va_start(args_stdout, fmt);

    vfprintf(fd, fmt, args_fd);
    vfprintf(stdout, fmt, args_stdout);

    fflush(fd);
    fflush(stdout);

    va_end(args_fd);
    va_end(args_stdout);
}

int main()
{
    FILE *fp = fopen("out.txt", "w");

    send_string(fp, "hello, ");
    send_string(fp, "world %d times!\n", 42);

    fclose(fp);

    return 0;
}
neuro_sys
  • 805
  • 7
  • 13
  • Your version is dangerous, think what happens if you do `send_string(fp, "Hello %d");`. The fix to that problem is quite easy. – Pablo Apr 21 '18 at 14:28
  • @Pablo Thanks for pointing out, I changed it to support variadic arguments. – neuro_sys Apr 21 '18 at 14:40
  • hehe, that was my idea too. But the fix I talked about in your last code was: instead of `fprintf(fd, str);` do `fprintf(fd, "%s", str);`. That would have solved the problem. – Pablo Apr 21 '18 at 14:44
  • 1
    When using `va_args`, don't forget to call `va_end` on your varidic variables – Pablo Apr 21 '18 at 14:46
  • Thanks but only the original answer helped. While the new stuff may, or more like definitely is, good practice I don't actually understand it as well. – Papbad Apr 21 '18 at 17:23
  • You're welcome. You mean the way before it had `va_args` stuff? They are only needed if you need to pass parameters for formatting as in printf. If you separately ask or look up variadic args in C, this version will be very easy to understand. – neuro_sys Apr 21 '18 at 17:29
  • 1
    @neuro_sys After looking around I was able to understood it better. Thank you very much :) – Papbad Apr 21 '18 at 21:20
1

As melpomene points out in the comments, you can use any of the implementation shown in that answer. They are certainly clever but I think you can do without having to do forks and popens and relying on an external program like tee, specially if that tool is not available on your system.

I'd do this:

#include <stdarg.h>

int teeprintf(FILE *fp, const char *format, ...)
{
    int ret;
    va_list ap1, ap2;
    va_start(ap1, format);
    va_copy(ap2, ap1);

    vprintf(format, ap1);  // printing on stdout
    ret = vfprintf(fp, format, ap2);
    //fflush(fp);   // <-- you might want if you
                    // want to see the output of the file
                    // immediately

    va_end(ap1);
    va_end(ap2);

    return ret;
}

Then you can use it like a regular printf

FILE *fp = fopen("/tmp/log", "w");

if(fp == NULL)
    return 1;

teeprintf(fp, "%s %d\n", "Hello World", 3*19);
teeprintf(fp, "fp points to %p\n", (void*) fp);
...
Pablo
  • 13,271
  • 4
  • 39
  • 59