5

Is there some magic I can do with dup2 (or fcntl), so that I redirect stdout to a file (i.e., anything written to descriptor 1 would go to a file), but then if I used some other mechanism, it would go to the terminal output? So loosely:

  int original_stdout;
  // some magic to save the original stdout
  int fd;
  open(fd, ...);
  dup2(fd, 1);
  write(1, ...);  // goes to the file open on fd
  write(original_stdout, ...); // still goes to the terminal
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
foxcub
  • 2,517
  • 2
  • 27
  • 27

1 Answers1

9

A simple call to dup will perform the saving. Here is a working example:

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

int main()
{
  // error checking omitted for brevity
  int original_stdout = dup(1);                   // magic
  int fd = open("foo", O_WRONLY | O_CREAT);
  dup2(fd, 1);
  close(fd);                                      // not needed any more
  write(1, "hello foo\n", 10);                    // goes to the file open on fd
  write(original_stdout, "hello terminal\n", 15); // still goes to the terminal
  return 0;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • 2
    Might want to go with `STDOUT_FILENO` rather than the magic numbers. – EOF Feb 19 '16 at 23:37
  • @EOF The idea was to retain as much of the original code, which was using numeric constants. Also, in the context of Unix, the symbolic constant is equivalent to spelling out the number. Numeric references to file descriptors are regularly made from shells and scripting languages. – user4815162342 Feb 19 '16 at 23:40
  • Using the macro `STDOUT_FILENO` instead of `1` for the descriptor number would make the code much more readable; I think readability is extremely important. Regarding scripting languages, I disagree: for example `awk` and `sed` do not have any concept of file descriptors. gawk and mawk support special names `/dev/stdin`, `/dev/stdout`, and `/dev/stderr` (even in Windows). Those scripting languages that know about file descriptors, have inherited the concept (and largely interface) from C, and do have the named constants too (e.g. Perl, Python). – Nominal Animal Feb 20 '16 at 00:31
  • @NominalAnimal Readability is important, and so is education, especially in the context of the limited space of a SO answer. The provided snippet shows that the code runs *unchanged* with just the addition of `dup`. In time, the OP will learn about the symbolic constants, perhaps from these very comments. – user4815162342 Feb 20 '16 at 00:57
  • @user4815162342 Thanks for a great answer. Didn't realize it was so simple. I thought `dup2` would close stdout. – foxcub Feb 20 '16 at 05:27
  • @foxcub `dup2` *does* close the descriptor specified as its second argument (file descriptor 1), but not necessarily the underlying resource. One can think of file descriptors as handles to operating system resources, such as open files, TTYs, or network connections. `open` opens a file and returns a new handle; `dup` creates a new handles to an existing resource. The resources are reference-counted, and released only when the last handle is closed. So, calling `dup(1)` essentially *protects* existing stdout from being destroyed by `dup2(fd, 1)`. – user4815162342 Feb 20 '16 at 13:12
  • @user4815162342 Ah, brilliant. Thanks for the explanation. I wish `dup`'s manpage explained the reference counting. – foxcub Feb 20 '16 at 16:40
  • It is actually a bit more complicated than that. The kernel keeps *file descriptions* (reference-counted data structures, that include the current position for seekable files and devices), and an internal table of *file descriptors* for each process. Each entry in the file descriptor table refers to one file description. For userspace processes, *file descriptor* is a nonnegative integer, the kernel uses it as the index to the file descriptor table. `dup()` copies the reference to the *file description* to a new *file descriptor* table entry, and returns the new *file descriptor*. ... – Nominal Animal Feb 20 '16 at 23:20
  • `dup2()` replaces a specific *file descriptor*, by first closing the target *file descriptor* (removing the reference to the *file description*), then copying the reference to the *file description* from the source descriptor to the target descriptor. `fork()` copies the entire file descriptor table as-is to the new process. The [`man 2 dup`](http://man7.org/linux/man-pages/man2/dup.2.html) and [`man 2 fork`](http://man7.org/linux/man-pages/man2/fork.2.html) at the [Linux manual pages online](http://man7.org/linux/man-pages/) have further info on this. – Nominal Animal Feb 20 '16 at 23:23
  • @NominalAnimal That's equivalent to my previous comment, with "underlying resource" being called "file description" in your explanation. The terminology is irrelevant (and specific to kernel implementation), the notions are important. – user4815162342 Feb 20 '16 at 23:36
  • The mention of `fork()` is interesting, though, and part of reason why `dup()` documentation doesn't explain the reference counting. Namely, calling `dup()` is *one* way to get different handles to the same resource. The other is by forking, and then there are other ways to transfer file descriptors between processes. I believe this topic is covered in detail by "Advanced Programming in the Unix environment" by Richard Stevens. – user4815162342 Feb 20 '16 at 23:36
  • @user4815162342: I don't mean to imply you are wrong; I just thought the added information might be useful to someone. *"file descriptor"* and *"file description"* are the most common terms used for these, not specific to any kernel implementation. Searching for both, ie. `"file descriptor" "file description"` yields good background info links, so the terminology is actually relevant. As to descriptor passing ([`man 3 cmsg`](http://man7.org/linux/man-pages/man3/cmsg.3.html)), it too is basically just a cross-process `dup()`. – Nominal Animal Feb 21 '16 at 02:02
  • Thanks @user4815162342, helped me a lot with a different question. I wasn't quite sure about the `close(fd);`, but for that purpose, an extra open fd seems to be negligible in python, if I'm not missing anything. If I am - I'd appreciate your input :) The question at https://stackoverflow.com/a/75116797/2840436 Thank you! – micromoses Jan 14 '23 at 09:04