How to determine if cout and cerr go to the same place
Asked Answered
W

2

6

Is there a way in C++ to tell if std::cout and std::cerr are pointing to the same destination?

That is, I'd like to be able to distinguish when the program is launched like

program or program > log 2>&1 or program &> log

versus

program > log or program 2> errors or program > log 2> errors

(The use case is a situation where we'd like error information to be printed to both stdout and stderr when they are separate, but want to print a slightly differently formatted output (not just a concatenation) if they both go to the same destination. -- Yes, I'm aware this isn't ideal, and isn't the officially recommended way to do things, and shouldn't be looked on as a standard way of doing things. But please just trust me, though, that we've taken time to think things through, and for our particular use case this is the best option.)

For our purposes, we can assume that nothing has been done with cout/cerr redirection within the program itself (just the typical shell-level command line redirection), so if there's C-level functionality which looks at stdout/stderr directly (rather than the std::cout and std::cerr streams proper), that would likely work too.

Wendt answered 26/6, 2019 at 14:46 Comment(5)
I'm curious. Why should your program care whether cout and cerr end up writing to the same destination or not?Bdellium
I have no idea about C++ standard library way to do that, but what about OS-specific API? On Windows, you can use GetStdHandle with GetFileNameFromHandle to get actual file names for both standard error and standard output; for sure, there are similar Linux functionality.Berberidaceous
@RSahu The program itself doesn't care - we and our users care. For our end-user use cases, we want to be sure that messages indicating what error has occurred end up in both stderr and stdout (because depending on how our users have or have not set up output redirection they may or may not see it in one or the other.) We could just print things once to each (effectively what we're doing currently), but if stdout and stderr go to the same location, it would be clearer and more user friendly if we could reformat things to remove the redundant printing.Wendt
This should absolutely not be the care of your program. Duplicating error information to all streams is a total antipattern and you've discovered why it doesn't work.Richardricharda
I also do not really understand your use case, if the caller of your program decides to have redundant information, then just let them. I mean stdout and stderr are seperate for a reason, if one deliberately decides to merge them, then you just get what you asked for... nevertheless it is an interesting questionSheffield
B
1

This is another case of the XY Problem.

What you are really trying to accomplish is:

For our end-user use cases, we want to be sure that messages indicating what error has occurred end up in both stderr and stdout (because depending on how our users have or have not set up output redirection they may or may not see it in one or the other.) We could just print things once to each (effectively what we're doing currently), but if stdout and stderr go to the same location, it would be clearer and more user friendly if we could reformat things to remove the redundant printing.

Given that objective, a cleaner mechanism would be to allow the user to specify where they would like the error messages to go to. E.g.

the-program --error-destination "stdout"
the-program --error-destination "stderr"
the-program --error-destination "stdout,stderr"
the-program --error-destination "/tmp/errro-messages.txt"
the-program --error-destination "stdout,/tmp/errro-messages.txt"

With the understanding that "stdout" and "stderr" are special destinations. Anything else is a file.

Bdellium answered 26/6, 2019 at 15:19 Comment(7)
No, what I'm wanting to do is get reasonably formatted error messages in the default case, without requiring my end users to jump through extra hoops.Wendt
@R.M., That's always the right thing to do. That should not preclude you from allowing the user to make choices.Bdellium
I'm not precluding adding an option to specify where users want error info. However, that still leaves the question of what to do when they don't specify such an option. In such cases, the preferred behavior leads us back to the original question: printing one thing to stdout & stderr when they are different streams, but printing a differently formatted thing (not just a concatenation) when they're the same destination.Wendt
@R.M., the default behaviour should be to print error message to stderr/std::cerr only. All other output should go to stdout/std::cout. Adding more fine grained logic in your code does not make sense to me.Bdellium
While I agree in an abstract, theoretical sense, with our particular program, with our particular end-users, and with their particular use cases, printing error messages to both stdout & stderr is definitely the preferred behavior. While from an abstract theoretical sense it might be nice to tell our end users "Tough shit. Error messages default to stderr. Suck it up and deal." that doesn't work out well in practice.Wendt
also, don't forget /dev/tty where it is available. That isn't very likely to be redirectable to a file; so it's one way to make sure the user sees the error IF that is what you want to do.Josselyn
Regardless of whether OP's use case is XY, I would still like an answer to the question, because I have a case that I do not think is XY: I need my logs writes to be atomic for greppability, so I need my stdout and stderr writes to share a mutex if they go to the same place -- but I want to use separate mutexes if they don't, to avoid the performance hit and deadlock hazard.Adey
A
-1

If you really want to know on linux, /proc/self/fd will reveal all! Try ls -l /proc/self/fd today...

Also, if you really want to emit an error to the console, having determined the redirects have stolen your output, then you could always send it to /dev/tty. Don't bother with /dev/stderr as that is just a link to /proc/self/fd/2! The one issue with this is if your program is detached from the console with hup or similar then you can't use /dev/tty obviously.

Aleda answered 26/6, 2019 at 16:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.