当stdin是管道时如何从设备读取

So I have a Go program that reads from STDIN as such below. I want the username and password to be entered from keyboard or device but the string slice can be passed using pipe. If I run the command as below:

echo "Hello World" | go run main.go

os.Stdin will be set to read from pipes and never the keyboard. Is there a way that I can change os.Stdin FileMode as such that it will be reading from device, i.e. keyboard for username and password?

I tried using os.Stdin.Chmod(FileMode) but received this error:

chmod /dev/stdin: invalid argument

func main() {
        var n = []string{}
        scanner := bufio.NewScanner(os.Stdin)
        fmt.Println("Please type anything with Newline Separated, empty line signals termination")
        for scanner.Scan() {
            h := scanner.Text()
            if h == "" {
                break
            }
            n = append(n, h)
        }
        if err := scanner.Err(); err != nil {
            fmt.Printf("Error in reading from STDIN: %v
", err)
        }

        reader := bufio.NewReader(os.Stdin)
        os.Stdout.WriteString("Username: ")
        username, err := reader.ReadString('
')
        if err != nil {
            fmt.Printf("Unable to read username: %v
", err)
        }
        username = strings.TrimSpace(username)

        os.Stdout.WriteString("Password: ")
        bytePassword, _ := terminal.ReadPassword(int(os.Stdin.Fd()))

        password := string(bytePassword)
        os.Stdout.WriteString("
")
    }

You can instead read from /dev/tty as this is always the terminal (if the program runs on a terminal). This is portable only to Unix-like systems (Linux, BSD, macOS, etc) and won't work on Windows.

// +build !windows

tty, err := os.Open("/dev/tty")
if err != nil {
    log.Fatalf("can't open /dev/tty: %s", err)
}
scanner := bufio.NewScanner(tty)
// as you were ...

Probably scanf could help, check this example:

https://play.golang.org/p/tteQNl0trJp

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Enter your name")

    var name string
    fmt.Scanf("%s", &name)
    fmt.Printf("name = %s
", name)
}

Something a little more elaborated to check if there is something to read from stdin and if not prompt the user:

https://play.golang.org/p/7qeAQ5UNhdQ

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    // check if there is somethinig to read on STDIN
    stat, _ := os.Stdin.Stat()
    if (stat.Mode() & os.ModeCharDevice) == 0 {
        var stdin []byte
        scanner := bufio.NewScanner(os.Stdin)
        for scanner.Scan() {
            stdin = append(stdin, scanner.Bytes()...)
        }
        if err := scanner.Err(); err != nil {
            if err != nil {
                log.Fatal(err)
            }
        }
        fmt.Printf("stdin = %s
", stdin)
    } else {
        fmt.Println("Enter your name")

        var name string
        fmt.Scanf("%s", &name)
        fmt.Printf("name = %s
", name)
    }
}