将stderr紧急事件重定向回go中的终端

I'm trying to redirect panic/standard error back to bash terminal with the following go code:

    if err := syscall.Dup2(-1,int(os.Stderr.Fd())); err != nil {
      log.Fatalf("Failed to redirect stderr to bash: %v",err)
    }

But the err gives me a "bad file descriptor", probably because of the -1 I used in the first argument. I chose the value -1 because I found that int(file.(*os.File).Fd()) returned a -1 when file.Close() has been called.

As for what I'm trying to do. Elsewhere in my program, I had called a syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())), which logs stderr to an external file. But I want stderr to point to bash terminal on occasion.

https://golang.org/pkg/syscall/ Doesn't give a verbose explanation of syscall.Dup2. I started poking around what file.Fd() returns and how it is used, but also didn't fully understand.

Can anyone tell me how to redirect stderr back to bash terminal?

You have to save the original stderr somewhere before overwriting it with your first Dup2. (In general, it’s a bad idea to use the dup2 feature of closing the target, since it can’t report errors therefrom.) Then you can Dup2 that back to int(os.Stderr.Fd()) (aka 2).

I don't know why, but syscall.Dup2(0,int(os.Stderr.Fd())) returns panic stderr back to terminal.

My understanding of the linux operating system is weak. So I don't understand the significance of 0 in this context and the linux documentation.

Also, I haven't attempted this approach on a windows machine, so not sure what will happen there. I hope other people give better answers.

Can anyone tell me how to redirect stderr back to bash terminal?

In general, that is impossible, because a Unix program can be started (or run) without any terminal. For example, it could be started by a crontab(5) job, or thru some at or ssh command, etc. Think also of your program being run with redirections or in a pipeline, or of your program being run in a server (e.g. inside a data center); then it is likely to not have any terminal.

The common practice is for the user of your program to perhaps redirect stderr (and probably not to a terminal, but more likely to some file). Your user would use its shell for that purpose (e.g. run yourprogram 2> /tmp/errorfile; read the documentation of bash about redirections)

Terminals are quite complex stuff. You could read the TTY demystified page. See also pty(7) and termios(3). The usual way to handle terminals (on Unix) is by using the ncurses library (which has been wrapped as goncurses in Go).

Elsewhere in my program, I had called a syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd())), which logs stderr to an external file.

That is really a bad idea. Your user expects his/her stderr to stay the same (and would have redirected the stderr in his/her shell if so needed). Conventionally, you should not mess that standard streams in your program (and leave them to what they are).

A file descriptor is some small positive-or-zero index (into the file descriptor table of your process). System calls like dup2(2) are expecting valid file descriptors, and Go's syscall.Dup2 is just wrapping that dup2(2).

On Linux, you can query the file descriptor table of some process of pid 1234 by looking into the /proc/1234/fd/ directory. See proc(5) for more.

If you are absolutely certain that your program is running in a terminal, you might open /dev/tty to get it. See tty(4) for more. However, I don't recommend doing that (because you'll better design your program to be runnable outside of any terminal).

You may want to read some Linux programming book, such as ALP.

For logging purposes, Go provides its log package. See also syslog(3) and the log/syslog package of Go.

PS. I don't know Windows, but I believe it also can start programs without any terminal, e.g. as a background process. So even on Windows I would try to avoid doing that (redirection of stderr to a terminal).