In Go, is it possible to write to the console and read back?

2.3k views Asked by At

I'm writing a wizard for a CLI in Go. What I'd like to do is ask the user what he wants to do, prepare the appropriate CLI command, and write it to the console. The user then would submit the command to the CLI by pressing Enter, possibly after editing it first. In other words, I want to write output to stdout that becomes input to stdin when the user presses Enter. Is there a way to do this in Go?

2

There are 2 answers

2
NeoWang On

For getting input directly from user:

var s string
_, err := fmt.Scanf("%s", &s)

For curses-like application, look here: https://github.com/rthornton128/goncurses/blob/master/ncurses.go

It has C bindings.

0
David Tootill On

I've found a Go command line editor package, https://github.com/peterh/liner, that incorporates the capability requested and a good deal more. It allows you to write a CLI with history and command completion, with the command completion feature providing exactly what I asked for in the question: it presents text that the user can edit and submit to the CLI. It supports Windows, Linux, and Mac. Here's a slightly modified version of the example in its README that runs a simple CLI with command completion. For example, the user can type "j" and press Tab to cycle through a list of names, edit one to taste, and press Enter to submit.

package main

import (
    "fmt"
    "log"
    "os"
    "strings"

    "github.com/peterh/liner"
)

var (
    history_fn = ".liner_history"
    names      = []string{"jack", "john", "james", "mary", "mike", "nancy"}
)

func main() {
    line := liner.NewLiner()
    defer line.Close()

    line.SetCompleter(func(line string) (c []string) {
        for _, n := range names {
            if strings.HasPrefix(n, strings.ToLower(line)) {
                c = append(c, n)
            }
        }
        return
    })

    if f, err := os.Open(history_fn); err == nil {
        line.ReadHistory(f)
        f.Close()
    }

    line.SetCtrlCAborts(true)
    for true {
        if name, err := line.Prompt("What is your name? "); err != nil {
            if err.Error() == "EOF" || err == liner.ErrPromptAborted {
                break
            }
            log.Print("Error reading line: ", err)
        } else {
            log.Print("Got: ", name)
            line.AppendHistory(name)
        }
    }
    fmt.Printf("End of test\n")

    if f, err := os.Create(history_fn); err != nil {
        log.Print("Error writing history file: ", err)
    } else {
        line.WriteHistory(f)
        f.Close()
    }
}