Netflix/go-expect

Is there a Interact() interface ?

cedarwu opened this issue · 2 comments

How to gives control of the child process to the interactive user (the human at the keyboard) like the interact method in expect/pexpect?
https://wiki.tcl-lang.org/page/interact

Send os.Stdin into the expect.Console. This library is a bit lower level so we expect users to decide how to manage the IO and spawn the child processes.

Can't make the child shell work as normal, could you please give some advice?
The shell works, but completion and ctrl-c does not work.

Code as following:
First, make expect to login into another machine, then forward all signals to subprocess and wait for finish.

package main

import (
	"log"
	"os"
	"os/exec"
	"os/signal"
	"syscall"

	expect "github.com/Netflix/go-expect"
)

func main() {
	c, err := expect.NewConsole(expect.WithStdout(os.Stdout), expect.WithStdin(os.Stdin))
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	cmd := exec.Command("ssh", "root@ip", "-i", "~/.ssh/id_rsa")
	cmd.Stdin = c.Tty()
	cmd.Stdout = c.Tty()
	cmd.Stderr = c.Tty()

	err = cmd.Start()
	if err != nil {
		log.Fatal(err)
	}

	_, err = c.ExpectString("[root@")
	if err != nil {
		log.Fatalln("expect err: [root@")
		return
	}

	// wait for the command to finish
	waitCh := make(chan error, 1)
	go func() {
		c.ExpectEOF()
		waitCh <- cmd.Wait()
		close(waitCh)
	}()

	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan)

	// You need a for loop to handle multiple signals
	for {
		select {
		case sig := <-sigChan:
			if err := cmd.Process.Signal(sig); err != nil {
				log.Println("error sending signal", sig, err)
			}
		case err := <-waitCh:
			log.Println("subprocess exited")
			var waitStatus syscall.WaitStatus
			if exitError, ok := err.(*exec.ExitError); ok {
				waitStatus = exitError.Sys().(syscall.WaitStatus)
				os.Exit(waitStatus.ExitStatus())
			}
			if err != nil {
				log.Fatal(err)
			}
			break
		}
	}
}