100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Expect and Automation Scripting

Learn how the Expect extension drives interactive command-line programs — logins, prompts, and legacy tools — programmatically using pattern-based dialogue control.

Practical Tcl/TkIntermediate9 min readJul 10, 2026
Analogies

What Expect Solves

Expect, created by Don Libes on top of Tcl, automates interaction with programs that expect a human at a terminal — logins, ftp, telnet, passwd, serial-port modems, or any CLI that prompts for input — by spawning a child process under a pseudo-terminal and scripting the back-and-forth dialogue that would otherwise require a person typing responses to prompts.

🏏

Cricket analogy: Expect scripting a login prompt is like a translator standing between two people who speak different pre-agreed signals — it 'spawns' the conversation and responds to each prompt exactly as a twelfth man would relay instructions from the dressing room to the field.

spawn, send, and expect Basics

The three core Expect commands are spawn (start a subprocess and attach it to a pseudo-terminal, e.g. spawn ssh user@host), expect (block until output matching a pattern appears, e.g. expect "password:"), and send (write text to the spawned process's stdin, e.g. send "$mypassword\r") — together they let a script drive an entire interactive session exactly as a human would type responses.

🏏

Cricket analogy: The spawn/expect/send cycle is like a captain (spawn) sending a fielder out, waiting for the umpire's specific signal (expect) before reacting, then relaying the next instruction (send) — a repeating loop of watch-then-respond.

Pattern Matching with expect

expect patterns can be plain strings, glob patterns, or full regular expressions via -re, and multiple alternatives are matched together with a single call: expect { "password:" { send "$pw\r" } "yes/no" { send "yes\r"; exp_continue } timeout { puts "login stalled"; exit 1 } }, where exp_continue re-enters the same expect block to keep matching further prompts without writing a new expect statement.

🏏

Cricket analogy: Matching several alternative patterns in one expect block is like a fielding captain having pre-set responses for every likely outcome of a delivery — dot ball, single, boundary — each triggering a different pre-agreed field adjustment.

tcl
#!/usr/bin/expect -f

set timeout 20
set host    [lindex $argv 0]
set user    [lindex $argv 1]
set pw      [lindex $argv 2]

spawn ssh -o StrictHostKeyChecking=no $user@$host

expect {
    "password:" {
        send "$pw\r"
        exp_continue
    }
    "yes/no" {
        send "yes\r"
        exp_continue
    }
    "$ " {
        # shell prompt reached; login succeeded
    }
    timeout {
        puts "ERROR: login timed out after 20s"
        exit 1
    }
    eof {
        puts "ERROR: connection closed unexpectedly"
        exit 1
    }
}

send "uptime\r"
expect "$ "
puts $expect_out(buffer)

send "exit\r"
expect eof

Timeouts, EOF, and Robust Control Flow

Robust Expect scripts always guard against timeout and eof as explicit branches inside every expect block, because a spawned process that hangs, crashes, or closes its connection otherwise leaves the script blocked forever with no error reported; set timeout 20 sets a default in seconds applied to subsequent expect calls, and per-block timeout/eof patterns let the script fail fast with a clear diagnostic instead of hanging a CI pipeline indefinitely.

🏏

Cricket analogy: Guarding every expect block with a timeout branch is like a third umpire enforcing a strict time limit on DRS reviews so the match doesn't stall indefinitely waiting for a decision that never comes.

Forgetting timeout and eof branches is the single most common cause of Expect scripts hanging forever in CI pipelines — a spawned ssh session waiting on an unexpected host-key prompt or a crashed remote shell will otherwise block the calling script (and any automation depending on it) with no error and no exit, silently consuming a CI runner slot.

Interact Mode and Common Pitfalls

After automation completes its scripted portion, interact hands control of the spawned process back to the real terminal user, letting a script do the tedious login and setup automatically and then drop the human into a live interactive session — useful for jump-host automation where a script authenticates through several hops before handing off a genuine shell to the operator.

🏏

Cricket analogy: interact handing control back to the human after automated setup is like a substitute fielder warming up in place of an injured player, then handing the position back once the original player is ready to take over.

interact accepts its own pattern-action pairs too, so you can keep scripted reactions active even while the human is typing — for example interact { "~exit" { send "logout\r"; return } } lets an operator type a custom escape sequence to end the session cleanly instead of relying on the shell's own exit command.

  • Expect drives interactive CLI programs by spawning them under a pseudo-terminal and scripting expected prompts and responses.
  • spawn, expect, and send form the core loop: start a process, wait for a pattern, write a response.
  • expect blocks can match multiple alternative patterns at once, including glob and -re regex patterns.
  • exp_continue re-enters the current expect block to handle further prompts without duplicating expect statements.
  • Every expect block should guard against timeout and eof to avoid hanging forever on a stuck or dropped connection.
  • interact hands control from the script back to a live human, useful for automated login followed by manual work.
  • Missing timeout/eof handling is the most common cause of Expect scripts hanging CI pipelines indefinitely.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#TclTkStudyNotes#ExpectAndAutomationScripting#Expect#Automation#Scripting#Solves#StudyNotes#SkillVeris#ExamPrep