In my work I’ve not used “Expect” many times, but to do some jobs I’ve learnt how to use it, and I must say that to complete some tasks this program can help a lot and be a valid alternative to more complex solutions, like a complete program in python, php or your favorite scripting program.
But first a description of expect taken from his man page:
Expect is a program that “talks” to other interactive programs according to a script. Following the script, Expect knows what can be expected from a program and what the correct response should be. An interpreted language provides branching and high-level control structures to direct the dialogue. In addition, the user can take control and interact directly when desired, afterward returning control to the script.
In this article I’ll show you 2 examples on how to use it, to connect to a remote server via ssh and issue a command, to change the password of a user with the command passwd.
In general, Expect is useful for running any program which requires interaction between the program and the user. All that is necessary is that the interaction can be characterized programmatically.
Expect uses Tcl (Tool Command Language). Tcl provides control flow (e.g., if, for, break), expression evaluation and several other features such as recursion, procedure definition, etc. To know exactly all the command supported by Expect check his man page and one of tcl.
Basic usage
expect [ -dDinN ] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ] |
Expect reads cmdfile for a list of commands to execute. Expect may also be invoked implicitly on systems which support the #! notation by marking the script executable, and making the first line in your script:
#!/usr/local/bin/expect |
In my examples I’ll use this method to create an expect script, I’ll put comments to describe how the actions/commands are implemented.
1 – Login with ssh to a remote server and give 1 command there with Expect
Create a new file, for example ssh.exp, and paste the following content, after that make it executable with the command
chmod +x ssh.exp |
ssh.exp:
!/usr/bin/expect -f #From Linuxaria.com the script is licensed under GNU GPL version 2.0 or above # Expect script to give username, password and IP/port # to connect to a remote ssh server and execute command. # This script needs five argument : # username = Unix username on the remote machine # password = Password of username you have provided. # server = IP Address or hostname of the remote server. # port = Port of the SSH server on the remote server, usually 22 # program = Complete path to the command you want to run #As first thing we check to have 5 arguments: if {[llength $argv] != 5} { # We give a message so the user know our syntax: puts "usage: ssh.exp username password server port program" #We exit with return code = 1 exit 1 } # Now we set variables in expect, note: [lrange $argv 0 0 =$1 the first parameter, and so on. set username [lrange $argv 0 0] set password [lrange $argv 1 1] set server [lrange $argv 2 2] set port [lrange $argv 3 3] set program [lrange $argv 4 4] #The value of timeout must be an integral number of seconds. Normally timeouts are nonnegative, but the special case of -1 signifies that expect #should wait forever. set timeout 60 # Now we can connect to the remote server/port with our username and password, the command spawn is used to execute another process: spawn ssh -p $port $username@$server $program match_max 100000 # Now we expect to have a request for password: expect "*?assword:*" # And we send our password: send -- "$password\r" # send blank line (\r) to make sure we get back to cli send -- "\r" #We have gave our "program" and now we expect that the remote server close the connection: expect eof |
Perfect now you can give from your terminal:
./ssh.exp root mysecretpw linuxaria.com 22 halt |
And shut down this blog 😉
2 – Use expect to change an user password.
It could be useful to change automatically (without interaction) the password for a given username, we can do this with expect
NOTE: this script must be run as root.
passwd.exp
#!/usr/bin/expect -f # In this script we just need 2 arguments, the username and the password if {[llength $argv] != 2} { # We give a message so the user know our syntax: puts "usage: /passwd.exp username password " #We exit with return code = 1 exit 1 } set username [lindex $argv 0] set newpass [lindex $argv 1] spawn passwd $username expect "?assword:" send \"$newpass\r\" expect "?assword:" send \"$newpass\r\" expect eof |
But don’t forget that you can use expect also inside a bash script, for example our passwd.exp could become a passwd.sh:
#!/bin/bash die () { echo >&2 "$@" exit 1 } [ "$#" -eq 2 ] || die "2 arguments required, $# provided" username=$1; newpass=$2; export HISTIGNORE="expect*"; expect -c " spawn passwd $username expect "?assword:" send \"$newpass\r\" expect "?assword:" send \"$newpass\r\" expect eof" export HISTIGNORE=""; |
This is useful when you have to do longer programs and you prefer to use bash.
Conclusions
These are 2 good examples on how flexible and useful expect can be to automate many small (and bigger things) from a simple command given to a remote server to the complete provisioning of an username/password on 1 (or more) unix systems.
Popular Posts:
- None Found
Complimenti!!!! grazie per questo interessantissimo articolo!
Oh…non sai da quanto tempo attendevo un articolo su questo argomento. Finalmente potrò sviluppare un automatismo per creare gli utenti postgres dai miei software.
Grazie 1 milione!!!
Felice che sia stato utile 😉
Excellent examples of how to automation CLI in expect. Have you looked at expect-lite, which is even easier to do CLI automation?
http://expect-lite.sf.net/
You’re missing the # in the shebang in your first script.
Thank you so much for the examples. I modified the code so it will work to add servers to known hosts with an automated ‘yes’. Sharing the code below.
#!/usr/bin/expect -f
set username
set yesval yes
set server
set timeout 1
spawn sftp $username@$server
match_max 100000
# Now we expect to have a request for password:
#expect “*?assword:*”
expect “*RSA key fingerprint*”
# And we send our password:
send — “$yesval\r”
# send blank line (\r) to make sure we get back to cli
send — “\r”
#We have gave our “program” and now we expect that the remote server close the connection:
expect eof
Hi Team,
I have a doubt in expect script
log_file -noappend /tmp/remote_new_execute1.log
spawn scp -o StrictHostKeyChecking=no /tmp/healthcheck.zip $env(user_name)@$env(first_node_ip):/tmp
expect “$env(user_name)@$env(first_node_ip)’s password:”
send — “$env(rem_password)\r”
expect “$”
spawn ssh -o StrictHostKeyChecking=no $env(user_name)@$env(first_node_ip)
expect “$env(user_name)@$env(first_node_ip)’s password:”
send — “$env(rem_password)\r”
expect “$”
send — “unzip /tmp/helathcheck.zip -d /tmp\r”
expect “replace collections.dat? \[y\]es, \[n\]o, \[A\]ll, \[N\]one, \[r\]ename:”
send — “A\r”
expect “$”
send — “sudo /tmp/healthcheck -pro hw\r”
expect “{\[sudo\] password for $env(user_name):}”
send — “$env(rem_password)\r”
set timeout 300
expect “*~]#*”
expect eof
The script is hanging after it executed the command sudo /tmp/healthcheck -pro hw.Also i have a doubt like for some of the hosts the terminal is $ and some pf them is having #,in that case how to handle the expect
when i enable exp_internal 1 on the above script it hangs at the point
Detailed report (html) – /tmp/healthcheck_011917_115841/healthcheck_xxx01_011917_115841.html\r\nUPLOAD(if required) – /tmp/healthcheck_xxx01_011917_115841.zip\r\n\r\n\r\n\r\n\r\n\r\n\u001b]0;exthost@xsd2db01:~\u0007exthost@xxx01 ~ $ ” (spawn_id exp6) match glob pattern “*~]#*”? no
expect: timed out
Could you please help
I hadn’t thought of using containers but that’s a great idea. Thanks so much for sharing!