Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Basic principles of using tcl-expect scripts


TCL-Expect scripts are an amazingly easy way to script out laborious tasks in the shell when you need to be interactive with the console. Think of them as a "macro" or way to programmaticly step through a process you would run by hand. They are similar to shell scripts but utilize the .tcl extension and a different #! call.

Setup Your Script

The first step, similar to writing a bash script, is to tell the script what it's executing under. For expect we use the following:


You also must place interact at the end, so your script looks like this:


... All your codez...


Puts & Output

Instead of the echo command, expect uses puts, which is pretty 1:1...

puts "I am performing a command..."

Would do exactly what you think it would, just echo out the text.

Now, the script will also show you the commands being performed. Good for testing, but maybe not required in "production" or daily-use. In that case you can just show the puts text via:

log_user 0

Which suppresses the commands and responses, showing only what you output via puts. You can always turn it back on later via:

log_user 1

If for example you wanted to show exactly what is coming back to the console.


Variables are very simple, just use set {name} {value}, for example:

set a "apple"
set b "banana"
set c "cantalope"

Referencing variables is done by simply appending $ to the name - so $a would contain apple.


If you want to set a variable by passing in an argument simply use the following:

set user [lindex $argv 0]
set password [lindex $argv 1]

Then, when running the script I could supply the varaible content via:

./myscript.tcl myuser mypassword

Spawn & Send

You can start a process with the spawn command. For example, ssh or scp are great examples:

set user "myuser"
set server ""

spawn ssh "$user\@server"

The above would spawn the ssh process and submit the user and server, so essentially like entering ssh in the console.

The send command allows you to send something to the console. For example, a password:

set password [lindex $argv 0]
set app [lindex $argv 1]

spawn sudo "apt-get install $app"

expect "assword"

send "$password\r"

The above would allow you to pass in your sudo password and the name of an application to install from apt, then wait for the password prompt and send it. No assword is not a mispelling, let's look at that expect statement...


The expect statement is where the magic happens. Let's start with the basic, say you run a command and just want to wait for the console to return to prompt before it moves on...

spawn apt-get "update"

expect "$"

# Move on to the next thing...

Very simple stuff, you spawn apt-get then just wait for the $ (or prompt).

The expect statement looks for a "close match" so in the above example your prompt (depending on your shell) could be something like root@server $~. The expect would find that $ and know that it's good to move forward.

So, how about more difficult cases, where you may not have a finite expect. Let's take a look at ssh again:

spawn ssh "$user/@server"

expect {
    "assword" {
        send "$password\r"
    "yes/no" {
        send "yes\r"

# NOW we can move on...

In the above example we could encounter two results. The first is that I have connected to the server previously and am prompted with Password: or Enter your password for server: (or something similar). The looseness of the assword define covers both cases and submits the $password variable. The "yes/no" would be if you're prompted to accept the remote host's key - it will submit yes for you, then wait for the password-prompt and handle that for you as well.

This can be adapted to different prompts as well - you could do something like the following to account for different styles of prompts:

expect {
    "> " { }
    "$ " { }

This can be built-upon more to really close the error-gap:

expect {
    "> " { }
    "$ " { }
    "assword: " { 
        send "$password\n" 
        expect {
            "> " { }
            "$ " { }
    "(yes/no)? " { 
        send "yes\n"
        expect {
          "> " { }
          "$ " { }
    default {
        send_user "Login failed\n"


If-Statments are extremely simple, and an example should be clear enough to make the point:

if { $a == "soup" } {
    puts "You have soup!"
else {
    puts "No soup for you!"


There are several methods for looping. I tend to take the while approach when possible:

set count 10;
while {$count > 0 } {
    puts "$count\n"
    set count [expr $count-1];

The above will simply loop backwards from 10 and echo-out the number.

Functions & Proc

What about code re-use? Oh yeah - expect has that:

proc do_something { a b c } {
    puts "You should buy some $a, $b, and $c!\n"

Can then be called with:

set running [do_something "apples" "bananas" "cantalopes"]

Which would output:

You should buy some apples, bananas, and cantalopes!"


The above describes the basics of expect scripting. It's really simple when you think about it in terms of taking actions you would perform by hand and converting them into a scripted set of actions.

I decided not to give some big example but rather cover the core concepts. I did this because when I got started the examples I saw just threw me off and once I spent some time digging to understand the fundamentals I quickly found myself writing the scripts with ease. Hope this helps you do the same!


This comment has been minimized.

Copy link

@adini121 adini121 commented Oct 31, 2015

Forked immediately! Awesome guide for a newbie!


This comment has been minimized.

Copy link

@beepony beepony commented Nov 22, 2015

It's easy to understand,thanks very much 👍


This comment has been minimized.

Copy link

@tajammulgm tajammulgm commented Dec 9, 2015

Thank you, really very helpful, learnt the things easily which I found difficulty for long time, once again Thank You.


This comment has been minimized.

Copy link

@shilpi230 shilpi230 commented Mar 2, 2016

How can I set variable to some shell command output ? I tried like this but this is wrong :/

set name [uname];


This comment has been minimized.

Copy link

@shilpi230 shilpi230 commented Mar 2, 2016

I got it. It is this set name [exec uname]; :)


This comment has been minimized.

Copy link

@mario20016 mario20016 commented Nov 2, 2016

Hi , I am struggling to pass "-" through expect ( for example if I am trying to get output for df -h ) , it throws error , it doesn't like "-" ,Please let me know if there is away to get around this , all my operations are based on that ,Thanks in advance


This comment has been minimized.

Copy link

@CosetteN CosetteN commented Nov 28, 2016

Mario20016 try using single quote marks instead of double: '-h'


This comment has been minimized.

Copy link

@Casteele72 Casteele72 commented Dec 6, 2016

Much of the Expect "scripting language" is actually simply the TCL programming language--Expect has very close ties to TCL in it's history. Generally, any valid TCL code/construct is valid in Expect. Any exceptions are documented. In the case of passing command lines which have a "-X" type option, it depends on how and where the - appears within the command string. In many, but not all cases, you can cause Expect/TCL to stop processing options with a "--" flag before passing the command string and command flags. In some cases, you may have to escape the - by following the rules of the TCL language. To give you more specific help on how to pass the -h, please explain which command you are using--In my system, spawn df -h works without problem, and returns the normal output of the df -h command when I issue the interact command to dump the output buffer:
casteelesa:~$ expect
expect1.1> spawn df -h
spawn df -h
expect1.2> interact
... (output of df -h here) ...


This comment has been minimized.

Copy link

@ghost ghost commented Dec 29, 2016

Its easy to understand!! Thank you!!....


This comment has been minimized.

Copy link

@Satyamjay Satyamjay commented Aug 16, 2017

Can we call an expect function from a bash script file?


This comment has been minimized.

Copy link

@nmanske nmanske commented Sep 5, 2017

@Satyamjay Yes it is possible to call an expect function using a bash script. You can call it in the format:
expect /home/user/my_expect_file.exp arg1 arg2


This comment has been minimized.

Copy link

@svenn71 svenn71 commented Jan 18, 2018

Alternative to
set count [expr $count-1];
incr count -1


This comment has been minimized.

Copy link

@loukya-amzn loukya-amzn commented Aug 16, 2019

Can we call an expect file from another expect file?

Here is my problem:
I have 2 expect files

From expect_file_1, i am using 'exec expect_file_2 $spawn_id'

I can see spawn_id getting carried from expect_file_1 to expect_file_2
But expect_file_2 is being executed as regular shell file, due to which any send or expect commands i have inside: 'expect_file_2' are giving me below error:

can not find channel named "exp5"
while executing
"expect {
-i $hhh
timeout { send_user "\nFailed to find listening\n"; exit 1 }
-re "AlexaClientHandler:LISTENING.*\r"
(procedure "initiateVerifyTapToTalk" line 6)
invoked from within
"initiateVerifyTapToTalk $wavfile $value"
(procedure "verifyTapToTalkWithoutText" line 2)
invoked from within
"verifyTapToTalkWithoutText whats_my_flash_briefing $processID"
(file "/home/autohutqa/Projects/aac-sdk/aac-integration/scripts/zoom/tests/flashbriefingtest1" line 6)
while executing
"exec $env(ZOOMHOME)/tests/flashbriefingtest1 $app_spawn_id"
(file "/home/autohutqa/Projects/aac-sdk/aac-integration/scripts/zoom/tests/logintest" line 6)


This comment has been minimized.

Copy link

@sidhussmann sidhussmann commented Jan 5, 2021

@loukya-amzn what you are looking for is to "source" other expect files:

So in expect_file_1:

source path_to/expect_file_2

Then you can call procedures that are defined in expect_file_2


This comment has been minimized.

Copy link

@huankimtran huankimtran commented Mar 21, 2021

Great guide!!!

Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment