xrfang
xrfang

Reputation: 2310

Is it possible to detect if a writer is tty or not?

Bash has a 'magical behavior', if you type 'ls', usually you will get colorful output, but if you redirect the output to a file, the color codes are gone. How to achive this effect using Go. e.g. With the following statement:

fmt.Println("\033[1;34mHello World!\033[0m")

I can see the text in color, but if I pipe the output to a file, the color is preserved, which is NOT what I want.

BTW, this question is mostly not related to Go, I just want to achive the effect in my go program.

Upvotes: 11

Views: 4629

Answers (3)

Alex Rudakov
Alex Rudakov

Reputation: 377

If you are just viewing a log file and don't want colors being preserved because it makes logs less readable, note that

less -r logfile
more -r logfile
multitail -cT ANSI logfile

will make these commands display ANSI colors.

There is also a vim plugin which does the same: https://github.com/powerman/vim-plugin-AnsiEsc

For some use cases this could be a simpler solution than upgrading an app to detect whether an output is attached to a tty.

Upvotes: 0

Jon Watte
Jon Watte

Reputation: 7208

I'm just answering by copying @ptx comment, which is the more canonical answer:

A better approach is to use term.IsTerminal from x/term. It works on windows and is maintained by the golang team

This is to raise visibility, as this answer is portable and doesn't add an external dependency.

package main

import (
    "os"
    "golang.org/x/term"
)

func main() {
    if term.IsTerminal(int(os.Stdout.Fd())) {
        // do stuff when we are attached to a tty
    } else {
        // do stuff when we are not attached
    }
}

Upvotes: 11

Arkadiusz Drabczyk
Arkadiusz Drabczyk

Reputation: 12393

Bash has a 'magical behavior', if you type 'ls', usually you will get colorful output, but if you redirect the output to a file, the color codes are gone.

It's not Bash feature, it's ls feature. It calls isatty() to check if stdout file descriptor refers to a terminal. In musl libc isatty is implemented like that:

int isatty(int fd)
{
        struct winsize wsz;
        unsigned long r = syscall(SYS_ioctl, fd, TIOCGWINSZ, &wsz);
        if (r == 0) return 1;
        if (errno != EBADF) errno = ENOTTY;
        return 0;
}

You can use the same method in Go:

package main

import (
        "fmt"
        "os"

        "golang.org/x/sys/unix"
)

func main() {
        _, err := unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ)
        if err != nil {
                fmt.Println("Hello World")
        } else {
                fmt.Println("\033[1;34mHello World!\033[0m")
        }
}

Upvotes: 7

Related Questions