Is there a Interact() interface
Hi,
How to gives control of the child process to the interactive user (the human at the keyboard) like the interact() method in pexpect?
Hey Kenny.
No there's no interact() interface in this expect , purely b/c we haven't had the usecase for it.
You'd like for a human to interact with the process until pressing some keycombo to give the control back to the expect batch?
Hi,
Use case as below, use expect to login by ssh and run batch commands, then give ssh control back to user so that user can start to work on ssh session.
Hey Kenny ..
Late response , pretty hectic atm. Happy to have Interact added to this package.
I'll sort out a design next week , feel free to add anything here that you'd like for the interact interface.
Or a pull request .. :)
Ok have put some thought/code into this ..
My thinking is to expose Reader and Writer interfaces from expecter ..
With this you can just read/write the Expecter..
So doing an interact thing to your current terminal should be as simple as.
e := Spawn(bla) .... e.Send("User") e.Expect(bla) e.Send("pass123) e.Expect("Logged in") ... // Would need some more logic to check for some key combo to end interact. // Maybe some term settings and such. go io.Copy(e,os.Stdin) go io.Copy(os.Stdout,e) ... e.Send("logout\n")
Still working on implementing this in a proper way , some changes to the Expect logic needed here.
Hi Skalle,
If providing API as pexpect, the use case will be as below e := Spawn(bla) .... e.Send("User") e.Expect(bla) e.Send("pass123) e.Expect("Logged in") e.Interact() // this gives control of the child process to the interactive user, so that the process receive the user keyboard input directly, and maybe suppot customization of readline ... // user can use Ctrl+D or Ctrl+C to close the session
Still some stuff to work out , code so far is in the intent branch.
With this change the Expecter implements the Reader/Writer interface for you to interact with.
I think this'd make it more powerful and versatile than just adding the Interact function , this way you can tie the I/O to whatever you want eg. a PTY from term or some remote network pipe. Gives you full control of how to end the interaction too , write a phrase or type some mysterious character.
We can still add the Interact function on top of the Reader/Writer as a convinence thing just using the os.Stdin/os.Stdout for I/O.
Status is still work-in-progress. Off for some rest and weekend now , will mess around more with this next week.
sounds great
Sorry for the long delay here .. Perf time at work so very limited time for this.
Haven't forgotten about this, just need to find some time.
Incase someone want to have a stab at this one, feel free..
Fine to use some of the code I wrote to handle this or start from scratch.
Hey,
Have you figured it out how to add an interactive session?
I'm also working on a similar project and I also need an interactive session after logging in through ssh using expect
Hey vegeta1509 .
Sorry but this has been put on the backburner .. I did write an interact version earlier when the bug was filed but it didnt' fit very well into the library. Added complexity and was more of a hack.
Could you describe what you need here a bit further?
When we run command ssh root@xyz, then it login to a server and returns a PTY with which user can interact and can run whatever command they want. I want something similar I want to run some command using expect just after login to the server and after that I want to attach a PTY so that user can interact.
In SpawnSSHPTY method the code sshclient creates a session and then closes it in the function itself
// SpawnSSHPTY starts an interactive SSH session and ties it to a local PTY, optionally requests a remote PTY. func SpawnSSHPTY(sshClient *ssh.Client, timeout time.Duration, term term.Termios, opts ...Option) (*GExpect, <-chan error, error) { if sshClient == nil { return nil, nil, errors.New("*ssh.Client is nil") } if timeout < 1 { timeout = DefaultTimeout } // Setup interactive session session, err := sshClient.NewSession() if err != nil { return nil, nil, err } e := &GExpect{ rcv: make(chan struct{}), snd: make(chan string), chk: func(e *GExpect) bool { if e.ssh == nil { return false } _, err := e.ssh.SendRequest("dummy", false, nil) return err == nil }, cls: func(e *GExpect) error { if e.ssh != nil { return e.ssh.Close() } return nil }, ssh: session, timeout: timeout, chkDuration: checkDuration, } for _, o := range opts { o(e) } if term.Wz.WsCol == 0 { term.Wz.WsCol = sshTermWidth } if term.Wz.WsRow == 0 { term.Wz.WsRow = sshTermHeight } if err := session.RequestPty(sshTerm, int(term.Wz.WsRow), int(term.Wz.WsCol), term.ToSSH()); err != nil { return nil, nil, err } inPipe, err := session.StdinPipe() if err != nil { return nil, nil, err } outPipe, err := session.StdoutPipe() if err != nil { return nil, nil, err } errPipe, err := session.StderrPipe() if err != nil { return nil, nil, err } if err := session.Shell(); err != nil { return nil, nil, err } // Shell started. errCh := make(chan error, 1) go e.waitForSession(errCh, session.Wait, inPipe, outPipe, errPipe) return e, errCh, nil }
I've tried creating the session in main function and then passed session instead of client in the function. And after running my commands using expect I tried changing the I/O pipes so that it can work as interactive PTY.
go io.Copy(os.Stdout, outPipe) go io.Copy(inPipe, os.Stdin) go io.Copy(errPipe, os.Stderr)
But it's not working properly, the server gives output sometimes, sometimes it doesn't. Many times it doesn't take input properly. So I think the I/O pipes are not transmitting data properly. If you could help me in figuring out correct way to attach these I/O pipes then that would be great help.
Anyone got a solution for interact in goexpect ?