goexpect: github.com/google/goexpect Index | Examples | Files

package expect

import "github.com/google/goexpect"

Package expect is a Go version of the classic TCL Expect.

Index

Examples

Package Files

expect.go

Constants

const (
    // BatchSend for invoking Send in a batch
    BatchSend = iota
    // BatchExpect for invoking Expect in a batch
    BatchExpect
    // BatchSwitchCase for invoking ExpectSwitchCase in a batch
    BatchSwitchCase
    // BatchSendSignal for invoking SendSignal in a batch.
    BatchSendSignal
)

BatchCommands.

const (
    // OKTag marks the desired state was reached.
    OKTag = Tag(iota)
    // FailTag means reaching this state will fail the Switch/Case.
    FailTag
    // ContinueTag will recheck for matches.
    ContinueTag
    // NextTag skips match and continues to the next one.
    NextTag
    // NoTag signals no tag was set for this case.
    NoTag
)
const DefaultTimeout = 60 * time.Second

DefaultTimeout is the default Expect timeout.

func Continue Uses

func Continue(s *Status) func() (Tag, *Status)

Continue returns the Continue Tag and status.

func Fail Uses

func Fail(s *Status) func() (Tag, *Status)

Fail returns Fail Tag and status.

func LogContinue Uses

func LogContinue(msg string, s *Status) func() (Tag, *Status)

LogContinue logs the message and returns the Continue Tag and status.

func Next Uses

func Next() func() (Tag, *Status)

Next returns the Next Tag and status.

func OK Uses

func OK() func() (Tag, *Status)

OK returns the OK Tag and status.

type BCas Uses

type BCas struct {
    // C holds the Caser array for the SwitchCase command.
    C []Caser
}

BCas implements the Batcher interface for SwitchCase commands.

func (*BCas) Arg Uses

func (bc *BCas) Arg() string

Arg returns an empty string , not used for SwitchCase.

func (*BCas) Cases Uses

func (bc *BCas) Cases() []Caser

Cases returns the Caser structure.

func (*BCas) Cmd Uses

func (bc *BCas) Cmd() int

Cmd returns the SwitchCase command(BatchSwitchCase).

func (*BCas) Timeout Uses

func (bc *BCas) Timeout() time.Duration

Timeout returns -1 , setting it to the default value.

type BCasT Uses

type BCasT struct {
    // Cs holds the Caser array for the SwitchCase command.
    C   []Caser
    // Tout holds the SwitchCase timeout in seconds.
    T   int
}

BCasT implements the Batcher interfacs for SwitchCase commands, adding a timeout option to the BCas type.

func (*BCasT) Arg Uses

func (bct *BCasT) Arg() string

Arg returns an empty string , not used for SwitchCase.

func (*BCasT) Cases Uses

func (bct *BCasT) Cases() []Caser

Cases returns the Caser structure.

func (*BCasT) Cmd Uses

func (bct *BCasT) Cmd() int

Cmd returns the SwitchCase command(BatchSwitchCase).

func (*BCasT) Timeout Uses

func (bct *BCasT) Timeout() time.Duration

Timeout returns the timeout in seconds.

type BCase Uses

type BCase struct {
    // R contains the string regular expression.
    R   string
    // S contains the string to be sent if R matches.
    S   string
    // T contains the Tag.
    T   func() (Tag, *Status)
    // Rt contains the number of retries.
    Rt  int
}

BCase with just a string is a bit more friendly to scripting. Implements the Caser interface.

func (*BCase) RE Uses

func (b *BCase) RE() (*regexp.Regexp, error)

RE returns the compiled regular expression.

func (*BCase) Retry Uses

func (b *BCase) Retry() bool

Retry decrements the Retry counter and checks if there are any retries left.

func (*BCase) String Uses

func (b *BCase) String() string

Send returns the string to send.

func (*BCase) Tag Uses

func (b *BCase) Tag() (Tag, *Status)

Tag returns the BCase Tag.

type BExp Uses

type BExp struct {
    // R contains the Expect command regular expression.
    R string
}

BExp implements the Batcher interface for Expect commands using the default timeout.

func (*BExp) Arg Uses

func (be *BExp) Arg() string

Arg returns the Expect regular expression.

func (*BExp) Cases Uses

func (be *BExp) Cases() []Caser

Cases always returns nil for the Expect command.

func (*BExp) Cmd Uses

func (be *BExp) Cmd() int

Cmd returns the Expect command (BatchExpect).

func (*BExp) Timeout Uses

func (be *BExp) Timeout() time.Duration

Timeout always returns -1 which sets it to the value used to call the ExpectBatch function.

type BExpT Uses

type BExpT struct {
    // R contains the Expect command regular expression.
    R   string
    // T holds the Expect command timeout in seconds.
    T   int
}

BExpT implements the Batcher interface for Expect commands adding a timeout option to the BExp type.

func (*BExpT) Arg Uses

func (bt *BExpT) Arg() string

Arg returns the Expect regular expression.

func (*BExpT) Cases Uses

func (bt *BExpT) Cases() []Caser

Cases always return nil for the Expect command.

func (*BExpT) Cmd Uses

func (bt *BExpT) Cmd() int

Cmd returns the Expect command (BatchExpect).

func (*BExpT) Timeout Uses

func (bt *BExpT) Timeout() time.Duration

Timeout returns the timeout in seconds.

type BSig Uses

type BSig struct {
    // S contains the signal.
    S syscall.Signal
}

BSig implements the Batcher interface for SendSignal commands.

func (*BSig) Arg Uses

func (bs *BSig) Arg() string

Arg returns the signal integer.

func (*BSig) Cases Uses

func (bs *BSig) Cases() []Caser

Cases always returns nil for BSig.

func (*BSig) Cmd Uses

func (bs *BSig) Cmd() int

Cmd returns the SendSignal command (BatchSendSignal).

func (*BSig) Timeout Uses

func (bs *BSig) Timeout() time.Duration

Timeout always returns 0 for BSig.

type BSnd Uses

type BSnd struct {
    S string
}

BSnd implements the Batcher interface for Send commands.

func (*BSnd) Arg Uses

func (bs *BSnd) Arg() string

Arg returns the data to be sent.

func (*BSnd) Cases Uses

func (bs *BSnd) Cases() []Caser

Cases always returns nil , not used for Send commands.

func (*BSnd) Cmd Uses

func (bs *BSnd) Cmd() int

Cmd returns the Send command(BatchSend).

func (*BSnd) Timeout Uses

func (bs *BSnd) Timeout() time.Duration

Timeout always returns 0 , Send doesn't have a timeout.

type BatchRes Uses

type BatchRes struct {
    // Idx is used to match the result with the []Batcher commands sent in.
    Idx int
    // Out output buffer for the expect command at Batcher[Idx].
    Output string
    // Match regexp matches for expect command at Batcher[Idx].
    Match []string
}

BatchRes returned from ExpectBatch for every Expect command executed.

type Batcher Uses

type Batcher interface {
    // cmd returns the Batch command.
    Cmd() int
    // Arg returns the command argument.
    Arg() string
    // Timeout returns the timeout duration for the command , <0 gives default value.
    Timeout() time.Duration
    // Cases returns the Caser structure for SwitchCase commands.
    Cases() []Caser
}

Batcher interface is used to make it more straightforward and readable to create batches of Expects.

var batch = []Batcher{

&BExpT{"password",8},
&BSnd{"password\n"},
&BExp{"olakar@router>"},
&BSnd{ "show interface description\n"},
&BExp{ "olakar@router>"},

}

var batchSwCaseReplace = []Batcher{

&BCasT{[]Caser{
	&BCase{`([0-9]) -- .*\(MASTER\)`, `\1` + "\n"}}, 1},
&BExp{`prompt/>`},

}

type Case Uses

type Case struct {
    // R is the compiled regexp to match.
    R   *regexp.Regexp
    // S is the string to send if Regexp matches.
    S   string
    // T is the Tag for this Case.
    T   func() (Tag, *Status)
    // Rt specifies number of times to retry, only used for cases tagged with Continue.
    Rt  int
}

Case used by the ExpectSwitchCase to take different Cases. Implements the Caser interface.

func (*Case) RE Uses

func (c *Case) RE() (*regexp.Regexp, error)

RE returns the compiled regular expression.

func (*Case) Retry Uses

func (c *Case) Retry() bool

Retry decrements the Retry counter and checks if there are any retries left.

func (*Case) String Uses

func (c *Case) String() string

Send returns the string to send if regexp matches

func (*Case) Tag Uses

func (c *Case) Tag() (Tag, *Status)

Tag returns the tag for this case.

type Caser Uses

type Caser interface {
    // RE returns a compiled regexp
    RE() (*regexp.Regexp, error)
    // Send returns the send string
    String() string
    // Tag returns the Tag.
    Tag() (Tag, *Status)
    // Retry returns true if there are retries left.
    Retry() bool
}

Caser is an interface for ExpectSwitchCase and Batch to be able to handle both the Case struct and the more script friendly BCase struct.

type Expecter Uses

type Expecter interface {
    // Expect reads output from a spawned session and tries matching it with the provided regular expression.
    // It returns  all output found until match.
    Expect(*regexp.Regexp, time.Duration) (string, []string, error)
    // ExpectBatch takes an array of BatchEntries and runs through them in order. For every Expect
    // command a BatchRes entry is created with output buffer and sub matches.
    // Failure of any of the batch commands will stop the execution, returning the results up to the
    // failure.
    ExpectBatch([]Batcher, time.Duration) ([]BatchRes, error)
    // ExpectSwitchCase makes it possible to Expect with multiple regular expressions and actions. Returns the
    // full output and submatches of the commands together with an index for the matching Case.
    ExpectSwitchCase([]Caser, time.Duration) (string, []string, int, error)
    // Send sends data into the spawned session.
    Send(string) error
    // Close closes the spawned session and files.
    Close() error
}

Expecter interface primarily to make testing easier.

type GExpect Uses

type GExpect struct {
    // contains filtered or unexported fields
}

GExpect implements the Expecter interface.

func Spawn Uses

func Spawn(command string, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)

Spawn starts a new process and collects the output. The error channel returns the result of the command Spawned when it finishes. Arguments may not contain spaces.

func SpawnFake Uses

func SpawnFake(b []Batcher, timeout time.Duration, opt ...Option) (*GExpect, <-chan error, error)

SpawnFake spawns an expect.Batcher.

func SpawnGeneric Uses

func SpawnGeneric(opt *GenOptions, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)

SpawnGeneric is used to write generic Spawners. It returns an Expecter. The returned channel will give the return status of the spawned session, in practice this means the return value of the provided Wait function.

func SpawnSSH Uses

func SpawnSSH(sshClient *ssh.Client, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)

SpawnSSH starts an interactive SSH session,ties it to a PTY and collects the output. The returned channel sends the state of the SSH session after it finishes.

func SpawnSSHPTY Uses

func SpawnSSHPTY(sshClient *ssh.Client, timeout time.Duration, term term.Termios, opts ...Option) (*GExpect, <-chan error, error)

SpawnSSHPTY starts an interactive SSH session and ties it to a local PTY, optionally requests a remote PTY.

func SpawnWithArgs Uses

func SpawnWithArgs(command []string, timeout time.Duration, opts ...Option) (*GExpect, <-chan error, error)

SpawnWithArgs starts a new process and collects the output. The error channel returns the result of the command Spawned when it finishes. Arguments may contain spaces.

func (*GExpect) Close Uses

func (e *GExpect) Close() error

Close closes the Spawned session.

func (*GExpect) Expect Uses

func (e *GExpect) Expect(re *regexp.Regexp, timeout time.Duration) (string, []string, error)

Expect reads spawned processes output looking for input regular expression. Timeout set to 0 makes Expect return the current buffer. Negative timeout value sets it to Default timeout.

func (*GExpect) ExpectBatch Uses

func (e *GExpect) ExpectBatch(batch []Batcher, timeout time.Duration) ([]BatchRes, error)

ExpectBatch takes an array of BatchEntry and executes them in order filling in the BatchRes array for any Expect command executed.

func (*GExpect) ExpectSwitchCase Uses

func (e *GExpect) ExpectSwitchCase(cs []Caser, timeout time.Duration) (string, []string, int, error)

ExpectSwitchCase checks each Case against the accumulated out buffer, sending specified string back. Leaving Send empty will Send nothing to the process. Substring expansion can be used eg.

Case{`vf[0-9]{2}.[a-z]{3}[0-9]{2}\.net).*UP`,`show arp \1`}
Given: vf11.hnd01.net            UP      35 (4)        34 (4)          CONNECTED         0              0/0
Would send: show arp vf11.hnd01.net

func (*GExpect) Options Uses

func (e *GExpect) Options(opts ...Option) (prev Option)

Options sets the specified options.

func (*GExpect) Read Uses

func (e *GExpect) Read(p []byte) (nr int, err error)

Read implements the reader interface for the out buffer.

func (*GExpect) Send Uses

func (e *GExpect) Send(in string) error

Send sends a string to spawned process.

func (*GExpect) SendSignal Uses

func (e *GExpect) SendSignal(sig os.Signal) error

SendSignal sends a signal to the Expect controlled process. Only works on Process Expecters.

ExampleGExpect_SendSignal shows the usage of the SendSignal call.

Code:

exp, r, err := Spawn("testdata/traptest.sh", 30*time.Second)
if err != nil {
    fmt.Printf("Spawn failed: %v\n", err)
    return
}
if match, buf, err := exp.Expect(signalsInstalled, time.Second*20); err != nil {
    fmt.Printf("exp.Expect failed, match: %v, buf: %v, err: %v", match, buf, err)
    return
}

if err := exp.SendSignal(syscall.SIGUSR1); err != nil {
    fmt.Printf("exp.SendSignal failed: %v", err)
    return
}

reExpect, err := regexp.Compile("USR1")
if err != nil {
    fmt.Printf("regexp.Compile(%q) failed: %v", "Sig USR1", err)
    return
}

match, buf, err := exp.Expect(reExpect, time.Second*20)
if err != nil {
    fmt.Printf("exp.Expect failed, match: %v, buf: %v, err: %v", match, buf, err)
    return
}
fmt.Println(match)
<-r

Output:

Got the USR1 Signal

func (*GExpect) String Uses

func (e *GExpect) String() string

String implements the stringer interface.

type GenOptions Uses

type GenOptions struct {
    // In is where Expect Send messages will be written.
    In  io.WriteCloser
    // Out will be read and matched by the expecter.
    Out io.Reader
    // Wait is used by expect to know when the session is over and cleanup of io Go routines should happen.
    Wait func() error
    // Close will be called as part of the expect Close, should normally include a Close of the In WriteCloser.
    Close func() error
    // Check is called everytime a Send or Expect function is called to makes sure the session is still running.
    Check func() bool
}

GenOptions contains the options needed to set up a generic Spawner.

type Option Uses

type Option func(*GExpect) Option

Option represents one Expecter option.

func ChangeCheck Uses

func ChangeCheck(f func() bool) Option

ChangeCheck changes the Expect check function.

ExampleChangeCheck changes the check function runtime for an Expect session.

Code:

rIn, wIn := io.Pipe()
rOut, wOut := io.Pipe()
waitCh := make(chan error)
outCh := make(chan string)
defer close(outCh)

go fakeCli(cliMap, rIn, wOut)
go func() {
    var last string
    for s := range outCh {
        if s == last {
            continue
        }
        fmt.Println(s)
        last = s
    }
}()

exp, r, err := SpawnGeneric(&GenOptions{
    In:    wIn,
    Out:   rOut,
    Wait:  func() error { return <-waitCh },
    Close: func() error { return wIn.Close() },
    Check: func() bool {
        outCh <- "Original check"
        return true
    }}, -1)
if err != nil {
    fmt.Printf("SpawnGeneric failed: %v\n", err)
    return
}
re := regexp.MustCompile("testrouter#")
interact := func() {
    for cmd := range cliMap {
        if err := exp.Send(cmd + "\n"); err != nil {
            fmt.Printf("exp.Send(%q) failed: %v\n", cmd+"\n", err)
            return
        }
        out, _, err := exp.Expect(re, -1)
        if err != nil {
            fmt.Printf("exp.Expect(%v) failed: %v out: %v", re, err, out)
            return
        }
    }
}
interact()
prev := exp.Options(ChangeCheck(func() bool {
    outCh <- "Replaced check"
    return true
}))
interact()
exp.Options(prev)
interact()

waitCh <- nil
exp.Close()
wOut.Close()

<-r

Output:

Original check
Replaced check
Original check

func CheckDuration Uses

func CheckDuration(d time.Duration) Option

CheckDuration changes the default duration checking for new incoming data.

func DebugCheck Uses

func DebugCheck(l *log.Logger) Option

DebugCheck adds logging to the check function. The check function for the spawners are called at creation/timeouts and I/O so can be usable for printing current state during debugging.

ExampleDebugCheck toggles the DebugCheck option.

Code:

rIn, wIn := io.Pipe()
rOut, wOut := io.Pipe()
rLog, wLog := io.Pipe()
waitCh := make(chan error)
defer rIn.Close()
defer wOut.Close()
defer wLog.Close()

go fakeCli(cliMap, rIn, wOut)

exp, r, err := SpawnGeneric(&GenOptions{
    In:    wIn,
    Out:   rOut,
    Wait:  func() error { return <-waitCh },
    Close: func() error { return wIn.Close() },
    Check: func() bool {
        return true
    }}, -1)
if err != nil {
    log.Errorf("SpawnGeneric failed: %v", err)
    return
}
re := regexp.MustCompile("testrouter#")
interact := func() {
    for cmd := range cliMap {
        if err := exp.Send(cmd + "\n"); err != nil {
            log.Errorf("exp.Send(%q) failed: %v\n", cmd+"\n", err)
            return
        }
        out, _, err := exp.Expect(re, -1)
        if err != nil {
            log.Errorf("exp.Expect(%v) failed: %v out: %v", re, err, out)
            return
        }
    }
}

go func() {
    var last string
    scn := bufio.NewScanner(rLog)
    for scn.Scan() {
        ws := strings.Split(scn.Text(), " ")
        if ws[0] == last {
            continue
        }
        last = ws[0]
        fmt.Println(ws[0])
    }
}()

fmt.Println("First round")
interact()
fmt.Println("Second round - Debugging enabled")
prev := exp.Options(DebugCheck(stdlog.New(wLog, "DebugExample ", 0)))
interact()
exp.Options(prev)
fmt.Println("Last round - Previous Check put back")
interact()

waitCh <- nil
exp.Close()
wOut.Close()

<-r

Output:

First round
Second round - Debugging enabled
DebugExample
Last round - Previous Check put back

func NoCheck Uses

func NoCheck() Option

NoCheck turns off the Expect alive checks.

func PartialMatch Uses

func PartialMatch(v bool) Option

PartialMatch enables/disables the returning of unmatched buffer so that consecutive expect call works.

func SendTimeout Uses

func SendTimeout(timeout time.Duration) Option

SendTimeout set timeout for Send commands

func SetEnv Uses

func SetEnv(env []string) Option

SetEnv sets the environmental variables of the spawned process.

func SetSysProcAttr Uses

func SetSysProcAttr(args *syscall.SysProcAttr) Option

SetSysProcAttr sets the SysProcAttr syscall values for the spawned process. Because this modifies cmd, it will only work with the process spawners and not effect the GExpect option method.

func Tee Uses

func Tee(w io.WriteCloser) Option

Tee duplicates all of the spawned process's output to the given writer and closes the writer when complete. Writes occur from another thread, so synchronization may be necessary.

func Verbose Uses

func Verbose(v bool) Option

Verbose enables/disables verbose logging of matches and sends.

ExampleVerbose changes the Verbose and VerboseWriter options.

Code:

rIn, wIn := io.Pipe()
rOut, wOut := io.Pipe()
waitCh := make(chan error)
outCh := make(chan string)
defer close(outCh)

go fakeCli(cliMap, rIn, wOut)
go func() {
    var last string
    for s := range outCh {
        if s == last {
            continue
        }
        fmt.Println(s)
        last = s
    }
}()

exp, r, err := SpawnGeneric(&GenOptions{
    In:    wIn,
    Out:   rOut,
    Wait:  func() error { return <-waitCh },
    Close: func() error { return wIn.Close() },
    Check: func() bool {
        return true
    }}, -1, Verbose(true), VerboseWriter(os.Stdout))
if err != nil {
    fmt.Printf("SpawnGeneric failed: %v\n", err)
    return
}
re := regexp.MustCompile("testrouter#")
var interactCmdSorted []string
for k := range cliMap {
    interactCmdSorted = append(interactCmdSorted, k)
}
sort.Strings(interactCmdSorted)
interact := func() {
    for _, cmd := range interactCmdSorted {
        if err := exp.Send(cmd + "\n"); err != nil {
            fmt.Printf("exp.Send(%q) failed: %v\n", cmd+"\n", err)
            return
        }
        out, _, err := exp.Expect(re, -1)
        if err != nil {
            fmt.Printf("exp.Expect(%v) failed: %v out: %v", re, err, out)
            return
        }
    }
}
interact()

waitCh <- nil
exp.Close()
wOut.Close()

<-r

Output:

Sent: "show system uptime\n"
Match for RE: "testrouter#" found: ["testrouter#"] Buffer: Current time:      1998-10-13 19:45:47 UTC
Time Source:       NTP CLOCK
System booted:     1998-10-12 20:51:41 UTC (22:54:06 ago)
Protocols started: 1998-10-13 19:33:45 UTC (00:12:02 ago)
Last configured:   1998-10-13 19:33:45 UTC (00:12:02 ago) by abc
12:45PM  up 22:54, 2 users, load averages: 0.07, 0.02, 0.01

testuser@testrouter#
Sent: "show system users\n"
Match for RE: "testrouter#" found: ["testrouter#"] Buffer: 7:30PM  up 4 days,  2:26, 2 users, load averages: 0.07, 0.02, 0.01
USER     TTY FROM              LOGIN@  IDLE WHAT
root     d0  -                Fri05PM 4days -csh (csh)
blue   p0 level5.company.net 7:30PM     - cli

testuser@testrouter#
Sent: "show version\n"
Match for RE: "testrouter#" found: ["testrouter#"] Buffer: Cisco IOS Software, 3600 Software (C3660-I-M), Version 12.3(4)T

TAC Support: http://www.cisco.com/tac
Copyright (c) 1986-2003 by Cisco Systems, Inc.
Compiled Thu 18-Sep-03 15:37 by ccai

ROM: System Bootstrap, Version 12.0(6r)T, RELEASE SOFTWARE (fc1)
ROM:

C3660-1 uptime is 1 week, 3 days, 6 hours, 41 minutes
System returned to ROM by power-on
System image file is "slot0:tftpboot/c3660-i-mz.123-4.T"

Cisco 3660 (R527x) processor (revision 1.0) with 57344K/8192K bytes of memory.
Processor board ID JAB055180FF
R527x CPU at 225Mhz, Implementation 40, Rev 10.0, 2048KB L2 Cache

3660 Chassis type: ENTERPRISE
2 FastEthernet interfaces
4 Serial interfaces
DRAM configuration is 64 bits wide with parity disabled.
125K bytes of NVRAM.
16384K bytes of processor board System flash (Read/Write)

Flash card inserted. Reading filesystem...done.
20480K bytes of processor board PCMCIA Slot0 flash (Read/Write)

Configuration register is 0x2102

testrouter#

func VerboseWriter Uses

func VerboseWriter(w io.Writer) Option

VerboseWriter sets an alternate destination for verbose logs.

type Status Uses

type Status struct {
    // contains filtered or unexported fields
}

Status contains an errormessage and a status code.

func NewStatus Uses

func NewStatus(code codes.Code, msg string) *Status

NewStatus creates a Status with the provided code and message.

func NewStatusf Uses

func NewStatusf(code codes.Code, format string, a ...interface{}) *Status

NewStatusf returns a Status with the provided code and a formatted message.

func (*Status) Err Uses

func (s *Status) Err() error

Err is a helper to handle errors.

func (*Status) Error Uses

func (s *Status) Error() string

Error is here to adhere to the error interface.

type Tag Uses

type Tag int32

Tag represents the state for a Caser.

type TimeoutError Uses

type TimeoutError int

TimeoutError is the error returned by all Expect functions upon timer expiry.

func (TimeoutError) Error Uses

func (t TimeoutError) Error() string

Error implements the Error interface.

Package expect imports 17 packages (graph) and is imported by 25 packages. Updated 2020-08-17. Refresh now. Tools for package owners.