Netflix/go-expect

[QUESTION] How do you run a command silently

WestleyK opened this issue · 2 comments

So, I have a test script

#!/bin/sh

echo -n "Enter your input: "

read foo

echo "You entered: ${foo}"

And then I have my go code:

package main

import (
	"log"
	"fmt"
	"os"
	"os/exec"
	"time"

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

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

	cmd := exec.Command("./sh.sh")
	cmd.Stdin = c.Tty()
	cmd.Stdout = c.Tty()
	cmd.Stderr = c.Tty()

	go func() {
		c.ExpectEOF()
	}()

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

	time.Sleep(time.Second)
	c.Send("hello world\n")
	time.Sleep(time.Second)

	err = cmd.Wait()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("done")
}

And it does work when I run it:

$ go run foo.go 
Enter your input: hello world
You entered: hello world
done

But can I run it silently? With no output, like:

$ go run foo.go 
done

Also is the time.Sleep(time.Second) delay necessary? That will take up a lot of time if theres a lot of commands running.

Hi @WestleyK , you can run it without sleep like so:

package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"

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

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

	cmd := exec.Command("./sh.sh")
	cmd.Stdin = c.Tty()
	cmd.Stdout = c.Tty()
	cmd.Stderr = c.Tty()

	done := make(chan struct{})
	go func() {
		defer close(done)

		c.ExpectString("Enter your input: ")
		c.SendLine("hello world")
		c.ExpectEOF()
	}()

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

	c.Tty().Close()
	<-done
	fmt.Println("done")
}

c.ExpectString line unblocks when it has read the text Enter your input and then responds with hello world. We wait for a EOF or Tty close which I do later as c.Tty().Close(). I setup a channel (or you can use a sync.WaitGroup) to wait the goroutine to exit before printing done.

$ time go run .
Enter your input: hello world
You entered: hello world
done
go run .  0.22s user 0.06s system 163% cpu 0.167 total

You don't have to multiplex the output to os.Stdout, just create the expect.NewConsole with no arguments:

        // ...
	c, err := expect.NewConsole()
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()
        // ...

The output then becomes:

$ go run .
done

Supper thanks! Works perfectly 👍