Package expect provides an expect-like interface to automate control of interactive applications through a pseudo-terminal.
This project is a fork of the original dead Netflix/go-expect, with the goal of providing cross-platform support (specifcally Windows) via charmbracelet/x/xpty.
- On Unix the console is backed by a classic pty pair, backed by creack/pty.
- on Windows by a ConPTY pseudo console, backed by charmbracelet/x/conpty.
package main
import (
"context"
"log"
"os"
"os/exec"
"time"
expect "github.com/pete-woods/go-expect"
)
func main() {
c, err := expect.NewConsole(
expect.WithStdout(os.Stdout),
expect.WithDefaultTimeout(10*time.Second),
)
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Start attaches the command to the console's pseudo-terminal on all
// platforms. (On Unix you can alternatively assign c.Tty() to the
// command's Stdin/Stdout/Stderr yourself.)
cmd := exec.Command("vi")
if err = c.Start(cmd); err != nil {
log.Fatal(err)
}
time.Sleep(time.Second)
c.Send("iHello world\x1b")
time.Sleep(time.Second)
c.SendLine(":q!")
// On Windows cmd.Wait does not work for processes attached to a ConPTY,
// so use expect.WaitProcess instead.
if err = expect.WaitProcess(context.Background(), cmd); err != nil {
log.Fatal(err)
}
// The console holds the pseudo-terminal open, so the process exiting
// does not produce an EOF by itself. Close the console to unblock
// ExpectEOF once you are done.
c.Close()
c.ExpectEOF()
}c.ExpectString("What is 1+1?")
c.SendLine("2")
c.ExpectEOF()Console.Tty()only exists on Unix: ConPTY has no slave file. UseConsole.Startto attach commands.SendLineterminates lines with"\n"on Unix and"\r"on Windows, where the ConPTY treats carriage return as enter.- Wait for processes with
expect.WaitProcessrather thancmd.Wait. - ConPTY output contains VT escape sequences (cursor movement, colors) in addition to the raw program output. String matchers still work because the program's text appears literally within the stream, but exact-match assertions on full output will differ from Unix.